CF848D Shake It!

首先将最小割转化为最大流。

\(f_{n,m}\)\(n\) 次操作最大流为 \(m\) 的图的个数, \(F_{n,m}\)\(n\) 次操作最大流大于 \(m\) 的图的个数

为了转移,令 \(g_{n,m}\) 为只扩展一次 \((s,t)\)\(n\) 次操作最大流为 \(m\) 的图的个数 , 同理 \(G_{n,m}\) 为最大流大于 \(m\) 的图的个数

发现 \(g\) 的流量取决于 \((s,w)\)\((w,t)\) 的流量,构成了一个子问题,不难得到:

\[G_{n,m}=\sum_{i=0}^{n-1} F_{i,m} F_{n-1-i,m} \]

同时有 \(g_{n,m}=G_{n,m}-G_{n,m+1}\)

现在只需考虑如何通过 \(g\) 得到 \(f\),它们之间的区别在于 \(f\) 可以扩展 \((s,t)\) 多次,换句话说, \(f\) 是由若干个 \(g\) 构成的

那么可以用类似背包的做法求得 \(f\),具体来说,可以枚举 \(g_{a,b}\) 出现次数 \(k\) ,它相当于在 \(g_{a,b}\) 个方案中选 \(k\) 个的方案数,由隔板法可知为 \(\binom{k-1+g_{a,b}}{k}\)

复杂度大概是 \(\mathcal O(n^4 \ln n)\)

#include <cstdio>

const int MAXN = 50 , Mod = 1e9 + 7;
int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
int Mul( int x , int y ) { return 1ll * x * y % Mod; }
int iv[ MAXN + 5 ];
void Init( ) {
	iv[ 1 ] = 1; for( int i = 2 ; i <= MAXN ; i ++ ) iv[ i ] = Mul( Mod - Mod / i , iv[ Mod % i ] );
}

int n , m , f[ MAXN + 5 ][ MAXN + 5 ] , F[ MAXN + 5 ][ MAXN + 5 ] , g[ MAXN + 5 ][ MAXN + 5 ] , G[ MAXN + 5 ][ MAXN + 5 ];
int dp[ MAXN + 5 ][ MAXN + 5 ];

int main( ) {
//	freopen("party.in","r",stdin);
//	freopen("party.out","w",stdout);
	
	Init();
	scanf("%d %d",&n,&m);
	
	f[ 0 ][ 1 ] = 1; dp[ 0 ][ 0 ] = 1;
	for( int i = 1 ; i <= n ; i ++ ) {
		//F
		for( int j = n + 1 ; j >= 1 ; j -- ) F[ i - 1 ][ j ] = Add( F[ i - 1 ][ j + 1 ] , f[ i - 1 ][ j ] );
		
		//G , g
		for( int j = n + 1 ; j >= 1 ; j -- ) {
			for( int k = 0 ; k < i ; k ++ )
				G[ i ][ j ] = Add( G[ i ][ j ] , Mul( F[ k ][ j ] , F[ i - 1 - k ][ j ] ) );
			g[ i ][ j ] = Sub( G[ i ][ j ] , G[ i ][ j + 1 ] );
		}
		
		//f
		for( int j = n + 1 ; j >= 1 ; j -- ) {
			for( int a = n ; a >= 0 ; a -- )
				for( int b = n + 1 ; b >= 0 ; b -- ) {
					int tmp = 0 , c = 1; //c=C(k-1+g[a][b],k)
					for( int k = 0 ; k * i <= a && k * j <= b ; k ++ ) {
						tmp = Add( tmp , Mul( dp[ a - k * i ][ b - k * j ] , c ) );
						c = Mul( c , Mul( k + g[ i ][ j ] , iv[ k + 1 ] ) );
					}
					dp[ a ][ b ] = tmp;
				}
		}
		for( int j = 1 ; j <= n + 1 ; j ++ ) f[ i ][ j ] = dp[ i ][ j - 1 ];
	}
	printf("%d\n", f[ n ][ m ] ); 
	return 0;
}
posted @ 2021-10-22 21:37  chihik  阅读(27)  评论(0编辑  收藏  举报