P10879 「KDOI-07」对树链剖分的爱 题解

题面

一道很好的DP题,看了题解才会的。首先我们考虑对于一颗固定的树有什么比较有前途的做法,注意到这道题的树每个点都满足 \(fa_i<i\) ,所以我们令 \(u>v\),那么 \(u\)\(v\) 之间的路径必定经过了 \(u\)\(fa_u\) 的边。我们就可以将这条边的边权加上 \(w\),然后将 \(u\) 跳到其父亲的位置上,重复下去。这种方法的好处是我们不需要对比两个点的深度,只需要比较编号大小,所以树的具体形态也就不重要了。

上面的方法很容易拓展一下得到这道题的DP做法,对于每一对 \((u,v)\),我们设 \(f_{i,j}\) 表示 \((u,v)\) 跳到状态 \((i,j)\) 的概率。转移也很简单,我们只需要枚举 \(i\) 可能的父亲,将 \(\frac{f_{i,j}}{r_i-l_i+1}\) 加到 \(f_{fa_i,j}\) 就可以了,这个转移是 \(O(n^3)\) 的,但是我们发现每次转移都可以看做是 \(f\) 数组连续一段上加一个相同的数,差分一下就可以做到 \(O(n^2)\)。但是我们需要对每组询问DP,总复杂度依然达到了 \(O(n^2m)\)

但是我们可以发现每组询问的转移之间是独立的,所以我们在一开始在 \(f_{u,v}\) 上加上 \(w\),然后直接倒序枚举进行转移就可以了,复杂度为 \(O(n^2+m)\)

code:

代码 ```cpp #include #define ll long long using namespace std; const int N=5e3+10,mod=998244353; ll f[N][12][12],S,T,inv[12],ans,k; char s[N],t[12]; ll qp(ll x,ll y) { ll res=1; while(y) { if(y&1) (res*=x)%=mod; (x*=x)%=mod,y>>=1; } return res; } ll C(ll x) { ll res=1; for(int i=0;iT) return (ans+=res*C(pos))%=mod,void(); for(int r=l;r<=T;r++) dfs(r+1,pos+1,res*f[S][l][r]%mod); } int main() { cin>>k;inv[1]=1; for(int i=2;i<=10;i++) inv[i]=inv[i-1]*qp(i,mod-2)%mod; scanf("%s%s",s+1,t+1); S=strlen(s+1),T=strlen(t+1); for(int i=1;i<=S;i++) for(int l=1;l<=T;l++) for(int r=l;r<=T;r++) { f[i][l][r]=f[i-1][l][r]; if(s[i]==t[r]) (f[i][l][r]+=(l==r?1:f[i-1][l][r-1]))%=mod; } dfs(1,0,1); cout<
posted @ 2025-04-16 16:56  Re_Star  阅读(18)  评论(0)    收藏  举报