競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 341E問題

ABC341E

解法

良い文字列である必要十分条件が, 悪い箇所が一つもないこと. つまり,悪い箇所の存在を高速に判定できれば良い. 変化した部分が少ないので,そこだけ更新.
答えるクエリでは,0,1が交互に並んでいるか (\in Bool) を判定する. 区間を反転するクエリでは,区間の内部は交互に並んでいるか (\in Bool) は変わらない. Bool に入っているか変化するのは,区間の両端だけ. i番目と i+1番目が同じか \(\in Bool\) を set に入れておく. \([l,r)\) に対して,set に入っているかを判定する.

実装

0-indexed, [l,r) で考える. \(v[i] \in Bool\) を,\(v[i] = true\) iff \(s[i] = s[i+1]\) とする. ただし,\(i = 0, N\) は使わない. \(s[l,r)\) が良いか判定するとき,調べるべきは\(v[{[l+1,r-1]}]\) の中に true が存在しないことを調べれば十分. これは,set の lower bound, upper bound で求まる.
\(l+1 \leq r-1\) とする.

\([ l+1, l+1, \cdots , l+1) \cdots [ r-1, r-1, \cdots , r-1) \)

の一番左の \([\) が lower bound(l+1), 一番右の \()\) が upper bound(r-1).

注意

\(r = l+1\) のとき,区間 \([l+1, r-1]\) は空になるので, 個別に判定.この場合は長さ 1の文字列なので,良い文字列になる.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n,q;
  cin >> n >> q;
  string s; cin >> s;
  set<ll> ng;
 
  // ng iff same i,i+1
  // 0,n は使わない
  rep(i,n-1) if(s[i]==s[i+1]) ng.insert(i+1);

  auto flip = [&](ll i) -> void {
    if(!in(i,1LL,(ll)n)) return;

    if(ng.find(i) != ng.end()){
      ng.erase(i);
    }else{
      ng.insert(i);
    }
  };
 

  // [l,r)
  rep(qi,q){
    ll ty, l,r; cin >> ty >> l >> r;
    --l;
    if(ty == 1){
      flip(l);
      flip(r);
    }else{
      l++, r--;
      auto it_l = ng.lower_bound(l);
      auto it_r = ng.upper_bound(r);
      yesno(l > r || it_l == it_r);
    }
  }

  return 0;
}