競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 285F問題

\(\def \set #1#2{\{ #1 \ \vert \ #2 \}}\)

ABC285F

解法

\(S,T\) の持つべき性質を調べる. \(S_{l,r} := S_{[l,r)}\) とおく. 文字列 \(U\) と \(a \in \Sigma := Alphabet\) に対して, \(cnt_{U}(a)\) を,文字列 \(U\) に現れる \(a\) の個数とする. すなわち, \(cnt_{U}(a) := \#\set{i \in Dom(U)}{ U_{i} = a }\) とする.

  • \(S_{l,r}\) はソートされている必要がある.
  • (\(S_{l,r}\) はソートされていると仮定して) for any \(c \in (S_{l}, S_{r-1})\) such that \(cnt_{S_{l,r}}(c) = cnt_{T}(c)\).

前者は, 隣同士だけ比べれば十分. 後者は, 任意の \(c \in \Sigma\) に対して \(cnt_{T}(c) = cnt{S}(c)\) が成り立つので, \(S\) だけで判定できる.

実装

後者は,segtree を \(\# \Sigma = 26\) 個持てば良い. 更新クエリは,順序は前後しか変わらないし,cnt も 1箇所だけ更新すれば十分.

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

ll op(ll a, ll b){ return a+b; }
ll e() { return 0; }

int main() {
  ll n;
  cin >> n;
  string s; cin >> s;
  ll q; cin >> q;
  rep(i,n) s[i] -= 'a';

  using T = segtree<ll,op,e>;
  T t_ord(n-1);
  vector<T> s_cnt(26, T(n));
  auto add_seg = [&](T &t, ll p, ll x){
    t.set(p, t.get(p) + x);
  };
  auto add_query = [&](ll p, ll x){
    add_seg(s_cnt[s[p]], p, x); // s_cnt[s[p]]
    if(p < n-1 && s[p] > s[p+1])  add_seg(t_ord, p, x);
    if(p-1 >= 0 && s[p-1] > s[p]) add_seg(t_ord, p-1, x); // コピペミス: p-1
  };
 
  rep(i,n) add_seg(s_cnt[s[i]], i, 1);
  rep(i,n-1) add_seg(t_ord, i, s[i] > s[i+1]);

  rep(qi,q){
    ll ty; cin >> ty;
    if(ty == 1){
      ll p; char c; cin >> p >> c;
      --p;
      c -= 'a'; // rem -= 'a'

      add_query(p,-1);
      s[p] = c;
      add_query(p,+1);

    } else{
      ll l,r; cin >> l >> r;
      --l;

      char cl = s[l];
      char cr = s[r-1];

      bool ok = true;
      srep(c,cl+1,cr){
        ok &= s_cnt[c].prod(l,r) == s_cnt[c].all_prod(); // all_prod を使えば,全体の個数は保持する必要が無い.
      }

      ok &= t_ord.prod(l,r-1) == 0;

      if(ok) cout << "Yes\n";
      else cout << "No\n";
    }
  }


 

  return 0;
}