[CF848D]Shake It!

Shake It!

题解

很容易将原来的要求 m − 1 m-1 m1次割得只剩 1 1 1条边的条件转化成整张图的最小割为 m m m
由于我们最开始已经有了一条连接 s s s t t t的边,所以我们只需要新增加 1 1 1的流量了。
由于该图的特殊性,我们每次操作都是相对于一条边,新增一个点,连接该边的两个端点,我们很容易想到一种 d p dp dp的方法。
我们记 d p i , j dp_{i,j} dpi,j表示对于一条初始边,我们能够增加 i i i条边使之增加 j j j的流的方案数。
但我们可以相对于一条边进行很多次操作,所以如果只用 d p dp dp进行转移是相当麻烦的,我们可以定义一个中间变量, g i , j g_{i,j} gi,j表示在我们只对于初始边增加一条边的情况下的方案数,显然,这是可以从 d p dp dp中转移过来的。
g g g的转移方程式:
g i , j = ∑ k = 0 i − 1 ∑ j ′ = 0 k ∑ j ′ ′ = 0 i − k − 1 [ min ⁡ ( j ′ , j ′ ′ ) = j − 1 ] d p k , j ′ d p i − k − 1 , j ′ ′ = ∑ k = 0 i − j ∑ j ′ = j − 1 k ( 1 + [ j ′ = j − 1 ] ) d p k , j ′ d p i − k − 1 , j − 1 g_{i,j}=\sum_{k=0}^{i-1}\sum_{j'=0}^{k}\sum_{j''=0}^{i-k-1}[\min(j',j'')=j-1]dp_{k,j'}dp_{i-k-1,j''}=\sum_{k=0}^{i-j}\sum_{j'=j-1}^{k}(1+[j'=j-1])dp_{k,j'}dp_{i-k-1,j-1} gi,j=k=0i1j=0kj=0ik1[min(j,j)=j1]dpk,jdpik1,j=k=0ijj=j1k(1+[j=j1])dpk,jdpik1,j1
这部分的转移时间复杂度是 O ( n 4 ) O\left(n^4\right) O(n4)

显然,我们求出 g g g之后还需要利用 g g g求出我们的 d p dp dp,但我们又得要求我们所有的标号是无序的不同的 g i , j g_{i,j} gi,j我们可以背包,但相同的就不能了,我们要让这内部也变成无序的。
我们可以考虑通过组合求出在同一个 g i , j g_{i,j} gi,j选择了多个的情况。
假设我们原来有个长度为 g i , j g_{i,j} gi,j的球序列,我要从中选出可重的 k k k个球,我们该怎么用组合数表示方案。
我们考虑一个一个球地选,不考虑选的顺序,每选一个就在选的这个后面在放一个新球,代表再选一次这个球。
这样的话,总共会有 n + k n+k n+k个球,而最后一个球是无论如何也选不到的,可以将其去掉,现在还有 n + k − 1 n+k-1 n+k1个球,我们会选择 k k k个,此时任意一种选球的方案都可以对应回原序列的一种选择方案,而且是一一对应的,所以我们可以用KaTeX parse error: Expected group after '\binom' at end of input: \binom{n+k-1,k}表示我们该问题的答案。
显然,如果一次转移选择了 k k k g i , j g_{i,j} gi,j中的方案,显然,我们最后转移的系数是 ( g i , j + k − 1 k ) \binom{g_{i,j}+k-1}{k} (kgi,j+k1)
我们可以像多重背包一样,枚举我们这次转移了多少个 g i , j g_{i,j} gi,j,通过 t m p tmp tmp转移 d p dp dp,枚举 k k k,让
d p i , j + = ( g i ′ , j ′ + k − 1 k ) d p i − i ′ k , j − j ′ k dp_{i,j}+=\binom{g_{i',j'}+k-1}{k}dp_{i-i'k,j-j'k} dpi,j+=(kgi,j+k1)dpiik,jjk
实际上我们枚举 k k k加转移会贡献的复杂度是 n 2 1 + n 2 2 + . . . + n 2 n = n 2 ln ⁡   n \frac{n^2}{1}+\frac{n^2}{2}+...+\frac{n^2}{n}=n^2\ln\,n 1n2+2n2+...+nn2=n2lnn
再加上先前枚举 i ′ , j ′ i',j' i,j n 2 n^2 n2,是 O ( n 4 ln ⁡   n ) O\left(n^4\ln\,n\right) O(n4lnn)

总时间复杂度 O ( n 4 ln ⁡   n ) O\left(n^4\ln\,n\right) O(n4lnn)

源码

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;     
const LL INF=0x3f3f3f3f3f3f3f3f;  
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,dp[105][105],tmp[105][105],fac[105],inv[105],f[105],d[105];
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=100;i++)
		fac[i]=1ll*i*fac[i-1]%mo,
		f[i]=1ll*(mo-mo/i)*f[mo%i]%mo,
		inv[i]=1ll*f[i]*inv[i-1]%mo;
}
int C(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
}
signed main(){
	read(n);read(m);m--;init();
	if(n<m){puts("0");return 0;}
	if(n==m){puts("1");return 0;}
	dp[0][0]=1;
	for(int j=1;j<=n;j++){
		for(int i=1;i<=j;i++)
			for(int l=i-1;l<j;l++)
				for(int k=i-1;k<j-l;k++)
					if(l^i-1)Add(tmp[j][i],2ll*dp[k][i-1]*dp[j-k-1][l]%mo,mo);
					else Add(tmp[j][i],1ll*dp[k][i-1]*dp[j-k-1][i-1]%mo,mo);
		for(int l=0;l<=j;l++){
			d[0]=1;
			for(int k=1;k<=n/j;k++)
				d[k]=1ll*f[k]*d[k-1]%mo*(tmp[j][l]+k-1)%mo;
			for(int j1=n;j1>=j;j1--)
				for(int i1=j1;i1>=0;i1--)
					for(int k=1;k<=j1/j;k++)
						Add(dp[j1][i1],1ll*d[k]*dp[j1-k*j][i1-k*l]%mo,mo);
		}
	}
	printf("%d\n",dp[n][m]);
	return 0;
}

谢谢!!!

posted @ 2021-10-12 17:10  StaroForgin  阅读(9)  评论(0)    收藏  举报  来源