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]);
}
posted @ 2020-02-02 14:59  Shiina_Mashiro  阅读(194)  评论(0编辑  收藏  举报