[CF848D]Shake It!

题目

传送门 to CF

思路

说出来你可能不信,我在考场上的思路是正解,但是复杂度算错了根本没打……

首先最开始只有一种操作,就是 s → w → t s\rightarrow w\rightarrow t swt 。然后你会发现,这两条边之上建造的图是独立的。所以肯定考虑 d p \tt dp dp 啊!

f ( n , m ) f(n,m) f(n,m) 为所求的答案, g ( n , m ) g(n,m) g(n,m) 为 “两截式” 的方案数(即 s → w → t s\rightarrow w\rightarrow t swt 之上建造 n − 1 n-1 n1 条点),则
g ( n , m ) = ∑ x 1 + x 2 = n − 1 ∑ min ⁡ ( y 1 , y 2 ) = m f ( x 1 , y 1 ) ⋅ f ( x 2 , y 2 ) g(n,m)=\sum_{x_1+x_2=n-1}\sum_{\min(y_1,y_2)=m}f(x_1,y_1)\cdot f(x_2,y_2) g(n,m)=x1+x2=n1min(y1,y2)=mf(x1,y1)f(x2,y2)
如果 f f f 已知,预处理后缀和,枚举 n , m , x 1 n,m,x_1 n,m,x1 即可求出 g ( n , m ) g(n,m) g(n,m),复杂度 O ( n 3 ) \mathcal O(n^3) O(n3) 。现在考虑怎么用 g g g 求出 f f f 呢?

很容易受到 e F ( x ) e^{F(x)} eF(x) 这种想法的干扰。可是这里并不是那样的。 e F ( x ) e^{F(x)} eF(x) 往往是 有标号 的指数型生成函数,因为它是依次分配 i d id id,所以交换分配 i d id id 的顺序也可能分配出相同的 i d id id,进而导致重复。

这里没有什么好的方法,干脆 组合数硬算。让 g ( x , y ) g(x,y) g(x,y) 对应的方案中选出 c x , y c_{x,y} cx,y 个(可重复选择),显然方案数是隔板法 ( g + c − 1 g − 1 ) {g+c-1\choose g-1} (g1g+c1),于是
f ( n , m ) = ∑ ⟨ c ⟩ [ ∏ ( g ( x , y ) + c x , y − 1 g ( x , y ) − 1 ) ] f(n,m)=\sum_{\langle c\rangle}\left[\prod{g(x,y)+c_{x,y}-1\choose g(x,y)-1}\right] f(n,m)=c[(g(x,y)1g(x,y)+cx,y1)]
当然要满足 ∑ c x , y ⋅ x = n \sum c_{x,y}\cdot x=n cx,yx=n ∑ c x , y ⋅ y = m − 1 \sum c_{x,y}\cdot y=m-1 cx,yy=m1,这里 y y y 缺的 1 1 1 就是 s → t s\rightarrow t st 直接提供 1 1 1 的流量。

考虑生成函数。记 G ( n , m ) = ∑ t = 0 + ∞ ( g ( n , m ) + t − 1 t ) ⋅ x t n ⋅ y t m G(n,m)=\sum_{t=0}^{+\infty}{g(n,m)+t-1\choose t}\cdot x^{tn}\cdot y^{tm} G(n,m)=t=0+(tg(n,m)+t1)xtnytm,则 f ( n , m ) = [ x n y m − 1 ]    ∏ G ( a , b ) f(n,m)=[x^ny^{m-1}]\;\prod G(a,b) f(n,m)=[xnym1]G(a,b) 。注意这里 a , b a,b a,b 可以是没有任何范围的。

现在请问,所有 G ( n , m ) G(n,m) G(n,m) 的项数之和是什么?我粗略计算了一下,是 ( n ln ⁡ n ) ( m ln ⁡ m ) (n\ln n)(m\ln m) (nlnn)(mlnm),两个都是调和级数。但是实际上它是
∑ min ⁡ ( n a , m b ) ⩽ ∑ a , b n a + ∑ a , b m b = n m ( ln ⁡ n + ln ⁡ m ) \sum\min\left({n\over a},{m\over b}\right)\leqslant\sum_{a,b}{n\over a}+\sum_{a,b}{m\over b}=nm(\ln n+\ln m) min(an,bm)a,ban+a,bbm=nm(lnn+lnm)
如果进行暴力卷积,每一项乘 n 2 n^2 n2 个数,复杂度 O ( n 4 ln ⁡ n ) \mathcal O(n^4\ln n) O(n4lnn) 。感觉过不了,然后它过了……我也想不出什么方法优化它。

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; '0'>c||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int x){
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}

const int MAXN = 205;
const int Mod = 1e9+7;
int f[MAXN][MAXN], suffix_sum[MAXN][MAXN];
int g[MAXN][MAXN]; // product of all G
int tmp[MAXN][MAXN], inv[MAXN];

int main(){
	rep(i,(inv[1]=1)<<1,MAXN-1)
		inv[i] = int(int_(Mod-Mod/i)*inv[Mod%i]%Mod);
	int n = readint(), m = readint();
	f[0][1] = 1; // no operation, 1 flow
	suffix_sum[0][1] = 1; // maybe useful
	int len_x = 0, len_y = 0;
	g[0][0] = 1; // init G = 1
	rep(i,1,n){ // operation times
		rep(j,1,i+1){ // amount of flow
			int now = 0; // current g[i][j] (DP)
			rep(x,0,i-1) // enumerate one of the parts
				now = static_cast<int>((now // accumulate
					+int_(f[x][j])*suffix_sum[i-1-x][j+1]
					+int_(f[i-1-x][j])*suffix_sum[x][j])%Mod);
			int p = 0, q = 0; // exponent
			for(int c=1,t=0; p<=n&&q<=n; p+=i,q+=j,++t){
				rep(x,0,len_x) rep(y,0,len_y) tmp[x+p][y+q] =
					int((tmp[x+p][y+q]+int_(c)*g[x][y])%Mod);
				c = int(int_(now+t)*inv[t+1]%Mod*c%Mod);
			}
			p -= i, q -= j; // the last tranfer
			len_x = min(len_x+p,n), len_y = min(len_y+q,n);
			rep(x,0,len_x) rep(y,0,len_y) // copy back
				g[x][y] = tmp[x][y], tmp[x][y] = 0;
			f[i][j] = g[i][j-1]; // transfer
		}
		suffix_sum[i][i+1] = f[i][i+1];
		drep(j,i,1) suffix_sum[i][j] =
			(suffix_sum[i][j+1]+f[i][j])%Mod;
	}
	printf("%d\n",f[n][m]);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值