前言:
好题 ,DP+组合+最大流(或者最小割)
题目大意:
给定
n
,
m
n,m
n,m,现在按照此规则生成无向图,初始我们有一张图
G
G
G,仅包含两个节点
s
,
t
s,t
s,t,以及连接
s
→
t
s\to t
s→t 的边。
每次你可以选择一条已经存在的边
(
u
,
v
)
(u,v)
(u,v),然后将往图中加入节点
w
w
w 和边
(
u
,
w
)
,
(
w
,
v
)
(u,w),(w,v)
(u,w),(w,v),这样操作
n
n
n 次,我们将得到一张图
G
′
G'
G′
定义
G
′
G'
G′ 合法,当且仅当
mincut
(
s
,
t
)
=
m
\textrm{mincut}(s,t)=m
mincut(s,t)=m,即
s
→
t
s\to t
s→t 的最小割为
m
m
m
求本质不同的合法的图
G
′
G'
G′ 的数量,答案对
1
0
9
+
7
10^9+7
109+7 取模。
其中,两张图
G
1
,
G
2
G_1,G_2
G1,G2 不同,当且仅当不存在一种作用于点集的置换,使得边对应相同,同时
s
,
t
s,t
s,t 对应相同。
解题思路:
第一眼感觉可以DP或者搜索
肯定选DP呀
于是
设计状态
直接有什么限制就设什么
f
i
,
j
表示操作
i
次,得到的最大流为
j
的图的个数
f_{i,j}表示操作i次,得到的最大流为j的图的个数
fi,j表示操作i次,得到的最大流为j的图的个数
初始状态:
初始图的形态为
因此
f
0
,
1
=
1
f_{0,1}=1
f0,1=1
状态转移
考虑用
f
f
f转移
f
f
f,发现行不通,还差点东西
我们考虑,最大流由什么贡献而来,如图
最大流
=
1
+
m
i
n
(
x
,
y
)
+
m
i
n
(
z
,
p
)
,
(
x
,
y
,
z
,
p
表示边上的流量
)
最大流=1+min(x,y)+min(z,p),(x,y,z,p表示边上的流量)
最大流=1+min(x,y)+min(z,p),(x,y,z,p表示边上的流量)
所以最大流由两部分贡献: 1.初始的1流量
\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad
2.
m
i
n
(
中转点到起点的流量,中转点到终点的流量
)
min(中转点到起点的流量,中转点到终点的流量)
min(中转点到起点的流量,中转点到终点的流量)
所以我们设
g
i
,
j
表示初始有
s
,
t
,
w
三个点,
s
−
w
,
t
−
w
两条边,经过
i
−
1
次(由于提前加入了
w
,所以操作次数为
i
−
1
)操作后,最大流为
j
的方案数
g_{i,j}表示初始有s,t,w三个点,s-w,t-w两条边,经过i-1次(由于提前加入了w,所以操作次数为i-1)操作后,最大流为j的方案数
gi,j表示初始有s,t,w三个点,s−w,t−w两条边,经过i−1次(由于提前加入了w,所以操作次数为i−1)操作后,最大流为j的方案数
若我们已求出了
g
i
,
j
g_{i,j}
gi,j,那我们可以枚举一个
t
t
t
故
f
n
,
m
<
−
f
n
−
t
∗
i
,
m
−
t
∗
j
∗
C
(
)
f_{n,m} <- \quad f_{n-t*i,m-t*j}*C()
fn,m<−fn−t∗i,m−t∗j∗C(),我们再乘上一个组合数即可
这个组合数的意义就是,在选择
t
t
t个形如
s
−
w
−
t
s-w-t
s−w−t且操作
i
−
1
i-1
i−1次后最大流为
j
j
j的方案数
我们的
g
g
g计算的就是供选择的集合的大小
用隔板法的思想,将
t
t
t个球放入
g
i
,
j
g_{i,j}
gi,j个箱子
所以
f
n
,
m
<
−
f
n
−
t
∗
i
,
m
−
t
∗
j
∗
C
(
g
i
,
j
+
t
−
1
,
t
)
f_{n,m} <- \quad f_{n-t*i,m-t*j}*C(g_{i,j}+t-1,t)
fn,m<−fn−t∗i,m−t∗j∗C(gi,j+t−1,t)
g的转移
用
f
f
f转移g
g
n
,
m
<
−
∑
i
=
0
n
−
1
∑
m
i
n
(
x
,
y
)
=
m
f
i
,
x
∗
f
n
−
1
−
i
,
y
g_{n,m} <- \quad \sum_{i=0}^{n-1}\sum_{min(x,y)=m}f_{i,x}*f_{n-1-i,y}
gn,m<−i=0∑n−1min(x,y)=m∑fi,x∗fn−1−i,y
就是枚举有
i
i
i次操作在
s
−
w
s-w
s−w上,
(
n
−
1
)
−
i
(n-1)-i
(n−1)−i次操作在
w
−
t
w-t
w−t上
这个转移是
O
(
n
2
)
O(n^2)
O(n2),但通过处理后缀和就可以做到
O
(
n
)
O(n)
O(n)
这是一个技巧,处理
m
i
n
卷积
min卷积
min卷积时都可以用,即设
f
i
,
j
′
为
f
i
,
j
到
f
i
,
n
+
1
的和
f'_{i,j}为f_{i,j} 到f_{i,n+1}的和
fi,j′为fi,j到fi,n+1的和就是后缀和的啦 然后设
g
i
,
j
′
也为
g
的后缀和函数
g'_{i,j}也为g的后缀和函数
gi,j′也为g的后缀和函数
就有
g
n
,
m
′
<
−
∑
i
=
0
n
−
1
f
i
,
m
′
∗
f
n
−
1
−
i
,
m
′
g'_{n,m}<-\quad \sum_{i=0}^{n-1}f'_{i,m}*f'_{n-1-i,m}
gn,m′<−i=0∑n−1fi,m′∗fn−1−i,m′
g g g数组差分一下 g ′ g' g′即可得到
复杂度分析
枚举
g
g
g转移
f
f
f是
O
(
n
4
)
O(n^4)
O(n4)
枚举
t
t
t是调和级数
O
(
α
)
O(\alpha)
O(α)
考虑最坏的情况,枚举
t
t
t达到
O
(
ln
n
)
O(\ln n)
O(lnn)
总的复杂度为
O
(
n
4
ln
n
)
O(n^4 \ln n)
O(n4lnn)
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55,mod=1e9+7;
int n,m;
int f[N][N],g[N][N],sf[N][N],sg[N][N],inv[N];
int main() {
scanf("%d%d",&n,&m); f[0][1]=sf[0][1]=1;
for(int i=(inv[0]=inv[1]=1)+1;i<=N-5;i++)
inv[i]=(1ll*inv[mod%i]*(mod-mod/i))%mod;
// for(int i=1;i<=50;i++) printf("%d:%d ",i,inv[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++)
for(int k=0;k<=i-1;k++)
sg[i][j]=(1ll*sg[i][j]+1ll*sf[k][j]*sf[(i-1)-k][j]%mod)%mod;
for(int j=1;j<=n+1;j++) g[i][j]=((sg[i][j]-sg[i][j+1])%mod+mod)%mod;
for(int j=1;j<=n+1;j++){
for(int k=n+1;k;k--) {
for(int l=n+1;l;l--){
int mul=1;
for(int t=1;t*i<=k && t*j<=l ;t++){
mul=(1ll*mul*(g[i][j]+t-1)%mod*inv[t]%mod)%mod;
f[k][l]=(1ll*f[k][l]+1ll*f[k-t*i][l-t*j]*mul%mod)%mod;
}
}
}
}
for(int j=n+1;j;j--) sf[i][j]=(1ll*f[i][j]+1ll*sf[i][j+1])%mod;
}
printf("%d\n",f[n][m]);
}
完