2025.3.29 NOIP模拟赛
Problem C. 传统艺能
Description
给定一个长为 \(n\) 的字符串,只含有 \(\texttt{A,B,C}\)。
\(Q\) 次操作,单点修改,区间查询 \([l,r]\) 中本质不同的子序列个数。
\(\mathcal{O(nq)}\) Solution
设 \(f_i\) 为 \(A[1\sim i]\) 中本质不同子序列个数。
如果 \(a_i\) 前面没有与其相同的字符,那么 \(f_i=2_{i-1}+1\)。
否则,如果仍然以上面的方式转移,考虑哪些子序列会被算重。
假设现在有两个子序列 \(\texttt{ABCB}\) 与 \(\texttt{ABCBC}\),且 \(a_i=\texttt{C}\)。那么如果给前面一个子序列接上 \(\texttt{C}\),那么就会重复。
令 \(lst_x\) 为 \(x\) 这个字符上一次出现的位置。我们把 \(A[1\sim lst_{a_i}]\) 中所有子序列拿出来,在后面接上 \(a_i\),可以得到所有重复的子序列。那么重复的个数应为 \(f_{lst_{a_i}-1}\)。
于是得到转移 \(f_i=2f_{i-1}-f_{lst_{a_i}-1}\)。
我们对每一次询问从 \(l\) 至 \(r\) 做一次 \(O(n)\) 的 dp 即可。
\(\mathcal{O(q\log n)}\) Solution
我们换一种 dp 方式:令 \(f_i\) 为 以 \(i\) 结尾 的子序列个数。
设字符串中在 \(i\) 之前出现的最后一个 \(\texttt{A,B,C}\) 的位置分别为 \(preA,preB,preC\)。
那么有转移:\(f_i=f_{preA}+f_{preB}+f_{preC}+1\)。
设整个字符串中出现的最后一个 \(\texttt{A,B,C}\) 的位置为 \(lstA,lstB,lstC\),答案为 \(f_{lstA}+f_{lstB}+f_{lstC}\)。
发现我们这个时候可以把每一个位置的 \(f\) 写为一个矩阵:
其中 \(fa_i,fb_i,fc_i\) 分别表示 \(i\) 处的 \(f_{lstA},f_{lstB},f_{lstC}\)。
那么对于 \(a_i=\texttt{A,B,C}\),分别列出转移矩阵:
线段树维护矩阵乘法即可。
int n,Q;
char a[N];const ll mod=998244353;struct Matrix{ll val[4][4];
}ept,MA,MB,MC,X,st;inline ll Mod(ll x){return (x>=mod)?(x-mod):(x);
}inline void Add(ll& x,ll y){x=Mod(x+y);
}void Init(){MA.val[0][0]=1,MA.val[0][1]=0,MA.val[0][2]=0,MA.val[0][3]=0;MA.val[1][0]=1,MA.val[1][1]=1,MA.val[1][2]=0,MA.val[1][3]=0;MA.val[2][0]=1,MA.val[2][1]=0,MA.val[2][2]=1,MA.val[2][3]=0;MA.val[3][0]=1,MA.val[3][1]=0,MA.val[3][2]=0,MA.val[3][3]=1;MB.val[0][0]=1,MB.val[0][1]=1,MB.val[0][2]=0,MB.val[0][3]=0;MB.val[1][0]=0,MB.val[1][1]=1,MB.val[1][2]=0,MB.val[1][3]=0;MB.val[2][0]=0,MB.val[2][1]=1,MB.val[2][2]=1,MB.val[2][3]=0;MB.val[3][0]=0,MB.val[3][1]=1,MB.val[3][2]=0,MB.val[3][3]=1;MC.val[0][0]=1,MC.val[0][1]=0,MC.val[0][2]=1,MC.val[0][3]=0;MC.val[1][0]=0,MC.val[1][1]=1,MC.val[1][2]=1,MC.val[1][3]=0;MC.val[2][0]=0,MC.val[2][1]=0,MC.val[2][2]=1,MC.val[2][3]=0;MC.val[3][0]=0,MC.val[3][1]=0,MC.val[3][2]=1,MC.val[3][3]=1;st.val[0][3]=1;
}Matrix operator*(Matrix x,Matrix y){Matrix z=ept;for(int k=0;k<4;k++)for(int i=0;i<4;i++)for(int j=0;j<4;j++)Add(z.val[i][j],x.val[i][k]*y.val[k][j]%mod);return z;
}Matrix tr[N<<2];void Pushup(int p){tr[p]=tr[p<<1]*tr[p<<1|1];
}void Buildtr(int p,int l,int r){if(l==r){if(a[l]=='A') tr[p]=MA;else if(a[l]=='B') tr[p]=MB;else tr[p]=MC;return;}int mid=(l+r)>>1;Buildtr(p<<1,l,mid);Buildtr(p<<1|1,mid+1,r);Pushup(p);
}void Update(int p,int l,int r,int x){if(l==r) return tr[p]=X,void();int mid=(l+r)>>1;if(x<=mid) Update(p<<1,l,mid,x);else Update(p<<1|1,mid+1,r,x);Pushup(p);
}Matrix Ask(int p,int l,int r,int L,int R){if(L<=l&&r<=R) return tr[p];int mid=(l+r)>>1;if(L<=mid&&mid<R)return Ask(p<<1,l,mid,L,R)*Ask(p<<1|1,mid+1,r,L,R);else if(L<=mid) return Ask(p<<1,l,mid,L,R);else return Ask(p<<1|1,mid+1,r,L,R);
}signed main(){read(n),read(Q);Init();scanf("%s",a+1);Buildtr(1,1,n);while(Q--){int op; read(op);if(op==1){int x; char c[3];read(x),scanf("%s",c);if(c[0]=='A') X=MA,Update(1,1,n,x);else if(c[0]=='B') X=MB,Update(1,1,n,x);else X=MC,Update(1,1,n,x);}else{int l,r; read(l),read(r);Matrix res=st*Ask(1,1,n,l,r);ll ans=Mod(Mod(res.val[0][0]+res.val[0][1])+res.val[0][2]);printf("%lld\n",ans);}}return 0;
}