E - Replace Digits Editorial
by
Mitsubachi
この問題は区間に対する更新/値の取得が必要です。
そこで、その $2$ つをそれぞれ $O(\log N)$ で行う 遅延評価セグメント木 ( lazy segment tree ) を使用します。
参考記事
では、ACLの lazy_segtree を用いてこの問題を解く方法を考えましょう。
まず、この問題は以下のように言い換えることができます。
説明の都合上 $f(l,r)$ を $A_l \times 10^{r-l-1} + A_2 \times 10^{r-l-2} + \cdots + A_{r-1}\ \mathrm{mod}\ 998244353$ とします。言い換えた問題において、クエリのたびに出力するのは $f(1,N+1)$ です。長さ $N$ の $1$ 桁の数字からなる数列 $A$ が与えられます。
$A_i$ を $A$ の $i$ 番目の要素として、現在は $A_1$ から $A_N$ まで全て $1$ です。
$Q$ 個のクエリを処理してください。 $i$ 番目のクエリは以下の通りです。
$A_{L_i}$ から $A_{R_i}$ までを全て $D_i$ に更新し、その時点での $A_1 \times 10^{N-1} + A_2 \times 10^{N-2} + \cdots + A_N$ を $\mathrm{mod}\ 998244353$ で出力する。
以下では半開区間を $[l, r) = \{l, l+1, \dots, r-1\}$ で表し、$A_{[l,r)}$ に対する情報を $s_{[l, r)}$ とします。
$s_{[l, r)}$ は $A_{[l,r)}$ に対する情報を表すので、 $f(l,r)$ が必要です。
$s_{[l,m)}$ と $s_{[m,r)}$ から $s_{[l, r)}$ を計算する、すなわち $f(l,m)$ と $f(m,r)$ から $f(l,r)$ を計算するにはどうすればいいでしょうか。 ( $l \leq m \leq r$ とします。)
$f(l,r)$ を表す式の $10$ の指数に注目することで、 $f(l,r) = f(l,m) \times 10^{r-m} + f(m,r)\ \mathrm{mod}\ 998244353$ が成立することが分かります。よって、 $s_{[l,r)}$ に$f(l,r)$ と区間の長さすなわち $r-l$ を持たせることでうまくいきそうです。
ACLの lazy_segtree には 同じ区間に対する $2$ つの更新クエリ $f,g$ を $1$ つの更新クエリとして扱う際の方法を composition(f,g) として定義する必要があります。
composition(f,g) の引数は $f$ が先ですが、これが表すものは $g$ を適用した後 $f$ を適用するというものなので注意してください。
今回の場合は更新クエリを $A$ の区間を全て $D$ に更新するとすれば、同じ区間に更新クエリ $g,f$ をこの順で適用させた場合 $f$ のみを適用させたのと変わりません。
これを composition(f,g) に反映させましょう。
これらより、以下のように定義すればよいことが分かります。ただし、この問題は出力クエリが全て $\mathrm{mod}\ 998244353$ なので、
using mint = modint998244353;
としておきます。
また、始めに mint 配列 ten,one を定義します。 ten[i] は $10^i$ を、 one[i] は $1$ を $i$ 個並べた数を表します。 (ともに $\mathrm{mod}\ 998244353$ です。)
S は $A_{[l,r)}$ に対する情報すなわち、 $s_{[l, r)}$ を表す型です。上までの結果より、以下のようにすれば問題ありません。
struct S{
mint sum;
int len;
};
op は $s_{[l,m)}$ と $s_{[m,r)}$ を引数として $s_{[l, r)}$ を求める関数です。以下のようにすれば問題ありません。S op(S a,S b){
return {a.sum*ten[b.len]+b.sum,a.len+b.len};
}
e は区間長 $0$ の区間に対応する情報です。長さ $0$ の区間は数字としてみたときに $0$ を表すので以下のようにすれば問題ありません。S e(){
return {0,0};
}
F は $A_{[l,r)}$ に対応するクエリの情報を表す型です。更新する数字のみが重要なので、以下のようにします。using F = int;
mapping は更新クエリ $f$ を $A_{[l,r)}$ に対応させた後の $s_{[l,r)}$ を表します。 $A_{[l,r)}$ は $f$ を表す数字 が $r-l$ 個続くので、それを反映させます。
f==0 のif文がついている理由はこの後登場する id を処理するためです。S mapping(F f,S s){
if(f==0) return s;
return {f*one[s.len],s.len};
}
composition は更新クエリ $g,f$ をこの順で同じ区間に適応させることを $1$ つの更新クエリとした際のものを表すものです。 $f$ が id ならば $g$ のみ、そうでなければ $f$ のみ反映されるので以下のようにします。F composition(F f,F g){
if(f==0) return g;
return f;
}
id は恒等写像を表す更新クエリ、すなわち何もしないクエリです。 $D_i$ になりえない $0$ にします。F id(){
return 0;
}
後はこれらを用いて lazy_segtree を作成すればACすることができます。
実装例(C++)
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace std;
using namespace atcoder;
using mint = modint998244353;
vector<mint> ten(211000),one(211000);
struct S{
mint sum;
int len;
};
S op(S a,S b){
return {a.sum*ten[b.len]+b.sum,a.len+b.len};
}
S e(){
return {0,0};
}
using F = int;
S mapping(F f,S s){
if(f==0) return s;
return {f*one[s.len],s.len};
}
F composition(F f,F g){
if(f==0) return g;
return f;
}
F id(){
return 0;
}
int main(){
//ten[i] := 100...0 (mod998244353)
//one[i] := 111...1 (mod998244353)
ten[0]=1,one[0]=0;
for(int i=0;i<210000;i++){
ten[i+1]=ten[i]*10;
one[i+1]=one[i]+ten[i];
}
int n,q;
cin>>n>>q;
//初期状態
vector<S> initial(n,{1,1});
lazy_segtree<S,op,e,F,mapping,composition,id> seg(initial);
while(q--){
int l,r,d;
cin>>l>>r>>d;
//要素は0から始まる
l--;
//l番目からr-1番目までをdに更新
seg.apply(l,r,d);
//出力
cout<<seg.all_prod().sum.val()<<endl;
}
}
posted:
last update:
