题目大意
给定n,m,最开始一个无向图中只有两个点s,t和连接它们的一条边。你需要进行n次操作,每次选择图中一条边(u,v),加入一个点i,并且添加两条边(u,i),(i,v)。
问最终有多少种不同构的图,满足其s-t最小割为m。模10^9+7输出
n,m≤50
分析
设f[i][j]表示i次操作,s-t最小割为j的方案数。
接下来你需要枚举五个数a,b,c,d,x(其中四元组(a,b,c,d)之前没有被枚举过),意义如下:
加入了x个这样的点:设这个点为v,其中s-v的最小割为b,在s-v为基础的子图上进行了a次操作;v-t最小割为d,在v-t为基础的子图上进行了c次操作。那么得到转移:
f[i][j]∗Cxf[a][b]∗f[c][d]+x−1—>f[i+x∗(a+c+1)][j+x∗min(b,d)]
其中组合数表示f[a][b]*f[c][d]种方案选了x次,一种方案可重复选。
考虑优化这个dp。我们发现a,b,c,d,x对状态的转移有一个特点:dp两维的增量相同。所以可以设
g[i][j]=∑[a+c==i]∗[min(b,d)==j]∗f[a][b]∗f[c][d]
。然后上面的转移变成:
f[i][j]∗Cxg[p][q]+x−1—>f[i+x∗p][j+x∗q]
g[p][q]是由a,c < p转移而来,所以可以顺序枚举p,q,并且更新f数组
枚举x由调和级数可知 总复杂度是log的
那么时间复杂度为
O(n4logn)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=52,mo=1e9+7;
typedef long long LL;
int n,m,f[N][N],g[N][N],h[N][N],Inv[N],ans;
int main()
{
scanf("%d%d",&n,&m);
Inv[1]=1;
for (int i=2;i<=n+1;i++) Inv[i]=(LL)Inv[mo%i]*(mo-mo/i)%mo;
f[0][1]=1;
for (int i=1;i<=n;i++) for (int j=1;j<=i+1;j++)
{
for (int p=0;p<i;p++)
{
for (int q=j;q<=p+1;q++)
{
g[i][j]=(g[i][j]+(LL)f[p][q]*f[i-p-1][j])%mo;
}
for (int q=j+1;q<=i-p;q++)
{
g[i][j]=(g[i][j]+(LL)f[p][j]*f[i-p-1][q])%mo;
}
}
memset(h,0,sizeof(h));
for (int p=0;p+i<=n;p++)
{
for (int q=1;q+j<=n+1;q++)
{
int C=1;
for (int t=1;p+i*t<=n && q+i*t<=n+1;t++)
{
C=(LL)C*(g[i][j]+t-1)%mo*Inv[t]%mo;
h[p+i*t][q+j*t]=(h[p+i*t][q+j*t]+(LL)C*f[p][q])%mo;
}
}
}
for (int p=i;p<=n;p++) for (int q=j;q<=p+1;q++) f[p][q]=(f[p][q]+h[p][q])%mo;
}
printf("%d\n",f[n][m]);
return 0;
}