题目
思路
说出来你可能不信,我在考场上的思路是正解,但是复杂度算错了根本没打……
首先最开始只有一种操作,就是 s → w → t s\rightarrow w\rightarrow t s→w→t 。然后你会发现,这两条边之上建造的图是独立的。所以肯定考虑 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
s→w→t 之上建造
n
−
1
n-1
n−1 条点),则
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=n−1∑min(y1,y2)=m∑f(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}
(g−1g+c−1),于是
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,y−1)]
当然要满足
∑
c
x
,
y
⋅
x
=
n
\sum c_{x,y}\cdot x=n
∑cx,y⋅x=n 与
∑
c
x
,
y
⋅
y
=
m
−
1
\sum c_{x,y}\cdot y=m-1
∑cx,y⋅y=m−1,这里
y
y
y 缺的
1
1
1 就是
s
→
t
s\rightarrow t
s→t 直接提供
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)+t−1)⋅xtn⋅ytm,则 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)=[xnym−1]∏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,b∑an+a,b∑bm=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;
}