CF848D Shake It!
作者:互联网
Link
我们考虑dp,设\(f_{i,j}\)表示进行\(i\)次操作后Mincut为\(j\)的方案数。
转移与背包类似,考虑枚举成对的\(f\)进行转移,对于\(f_{i,j}\),我们枚举\(f_{a,b},f_{c,d}\),使得\(f_{i,j}\)的\(s\)与\(f_{a,b}\)的\(s\)重合,\(f_{a,b}\)的\(t\)与\(f_{c,d}\)的\(s\)重合,\(f_{c,d}\)的\(t\)与\(f_{i,j}\)的\(t\)重合,然后再枚举添加\(x\)对\(f_{a,b}-f_{c,d}\)。
这样就有:\(f_{i,j}{f_{a,b}f_{c,d}+x-1\choose x}\rightarrow f_{i+x(a+c+1),j+x\min(b,d)}\)。
这样做的时间复杂度是\(O(n^6\log n)\)(枚举\(x\)的复杂度为调和级数),无法接受。
考虑优化,设\(g_{i,j}=\sum\limits[a+c+1=i\wedge\min(b,d)=j]f_{a,b}f_{c,d}\),这样我们就可以先求出\(g_{i,j}\)再更新\(f_{i,j}\),时间复杂度为\(O(n^4\log n)\),非常优秀。
#include<cstdio>
#include<cstring>
const int N=57,P=1000000007;
void mod(int&x){x-=P,x+=x>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int f[N][N],g[N][N],t[N][N],inv[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m),inv[0]=inv[1]=f[0][1]=1;
for(int i=2;i<=n;++i) inv[i]=mul(inv[P%i],P-P/i);
for(int i=1;i<=n;++i)
for(int j=1;j<=i+1;++j)
{
for(int k=0;k<=i-1;++k)
{
for(int l=j;l<=k+1;++l) mod(g[i][j]+=mul(f[k][l],f[i-1-k][j]));
for(int l=j+1;l<=i-k;++l) mod(g[i][j]+=mul(f[k][j],f[i-1-k][l]));
}
memset(t,0,sizeof t);
for(int k=0;k<=n;++k) for(int p=1;p<=k+1;++p) for(int q=1,s=1;k+q*i<=n;++q) s=mul(mul(s,g[i][j]+q-1),inv[q]),mod(t[k+q*i][p+q*j]+=mul(f[k][p],s));
for(int k=0;k<=n;++k) for(int l=1;l<=k+1;++l) mod(f[k][l]+=t[k][l]);
}
printf("%d",f[n][m]);
}
标签:log,int,CF848D,inv,重合,枚举,Shake,复杂度 来源: https://www.cnblogs.com/cjoierShiina-Mashiro/p/12252235.html