题面
一道很好的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<