Topo Counting
题目描述:
给定一种有向无环图———的晒肉架图
(
D
R
G
)
(DRG)
(DRG),由唯一参数
N
N
N控制。
D
R
G
DRG
DRG包含
N
N
N组顶点。第
i
i
i组
V
i
V^i
Vi包含
2
N
2N
2N个顶点:
V
1
i
,
V
2
i
,
⋯
,
V
2
N
i
V^i_1,V ^ i_2,\cdots,V ^ i_ {2N}
V1i,V2i,⋯,V2Ni
在
D
R
G
DRG
DRG中有两种类型的边:组内边(每组内的边)和内部-组边(组之间的边)。
组内边缘:对于第
i
i
i组,存在以下组内边缘:
(
V
j
i
,
V
j
+
N
i
)
(V ^ i_j,V ^ i_ {j+N})
(Vji,Vj+Ni),对于所有整数
j
j
j使得
1
≤
j
≤
N
1\le j\le N
1≤j≤N
(
V
j
i
,
V
j
+
1
i
)
(V ^ i_j,V ^ i_ {j+1})
(Vji,Vj+1i),对于所有整数
j
j
j使得
1
≤
j
≤
N
−
1
1\le j\le N-1
1≤j≤N−1或
N
+
1
≤
j
≤
2
N
−
1
N+1 \leq j \le 2N-1
N+1≤j≤2N−1
组间边缘:组边缘存在:
(
V
i
+
N
1
,
V
1
i
+
1
)
(V ^ 1_ {i + N},V ^ {i + 1} _1)
(Vi+N1,V1i+1),对于所有整数
i
i
i使得
1
≤
i
≤
N
−
1
1 \le i \le N-1
1≤i≤N−1;
(
V
i
1
,
V
1
+
N
i
)
(V ^ 1_ {i},V ^ {i} _ {1 + N})
(Vi1,V1+Ni),对于所有整数
i
i
i使得
2
≤
i
≤
N
2 \le i \le N
2≤i≤N
现在我们想知道以
N
N
N为参数的
D
R
G
DRG
DRG的拓扑序数。
有向图
G
=
(
V
,
E
)
G=(V,E)
G=(V,E)的拓扑顺序是
V
(
G
)
V(G)
V(G)的所有顶点的排列
v
p
1
,
v
p
2
,
⋯
,
v
p
∣
V
(
G
)
∣
)
v_ {p_1},v_ {p_2},\cdots,v_ {p_ {| V(G)|}} )
vp1,vp2,⋯,vp∣V(G)∣)使得对于所有
i
<
j
i <j
i<j,
(
v
p
j
,
v
p
i
)
∉
E
(
G
)
(v_ {p_j},v_ {p_i}) \not \in E(G)
(vpj,vpi)∈E(G)
为了避免计算巨大的整数,请对答案取模
M
M
M。
输入描述:
输入仅包含两个整数 N , M ( 1 ≤ N ≤ 3000 , N ∗ N ∗ 2 < M ≤ 2 30 ) N,M(1 \le N \le 3000,N * N * 2 <M \le 2 ^ {30}) N,M(1≤N≤3000,N∗N∗2<M≤230),并且 M M M是质数。
输出描述:
输出一个整数,表示答案。
样例:
样例输入1:
2 1073741789
样例输出1:
31
样例输入2:
3 1073741789
样例输出2:
7954100
思路:
首先我们画一张图
(
n
=
4
)
(n=4)
(n=4):(爬ppt(官方)的图)
从中我们可以发现:所有的子图都连向了第一号子图,仔细观察就会发现这个图就像一个晒肉架子…
n
n
n唯一确定了这张图。
看到这张图,我们忽略图与图之间的连边就可以分开考虑,然后
d
p
dp
dp求出所有子图
(
(
(肉
)
)
)的可能性,最后汇总即可,如果是分开的几个子图,设两个子图上的节点数为
d
1
d_1
d1,
d
2
d_2
d2,拓扑序的可能性为
a
a
a,
b
b
b,那么两个子图的可能性就是
C
d
1
+
d
2
d
1
a
b
C^{d1}_{d1+d2}ab
Cd1+d2d1ab,这是很好解决的,但是图中是连着的,所以我们要分几种情况考虑。
对于一个子图,进行拓扑时删掉的点的数量上面的肯定比下面的多,而拓扑限制只有与一号子图的第一条连边,不删掉这条边是无法得到下面这块“肉”的,所以我们的
d
p
dp
dp需要维护的东西比较多:当前第二排架子删到了第几个;第一排架子删到了第几个;维护的“肉”还剩多少。这时你会发现:我们写出的
d
p
dp
dp是一个三维的
d
p
dp
dp,超时。
哇我辛辛苦苦求出来的居然超时呜呜呜呜
于是我们又想到如何压缩
d
p
dp
dp的维度:
如果在一个完整的图中只删去了一个点,我们只需要一维:
如果只删去第一行的,那么只需要两维:
如果把第二行也删了,也只需要两维:
这样我们就成功的把
d
p
dp
dp压成了二维。
A C AC AC C o d e Code Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=3005;
const int MAXM=MAXN*MAXN*2;
int n,mod,dp[MAXN][MAXN],mul[MAXM ],inv[MAXM];
int ksm(int a,int b){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ret;
}
int C(int n,int m) {
if(n<m||m<0) return 0;
return 1ll*mul[n]%mod*inv[m]%mod*inv[n-m]%mod;
}
int Cat(int n,int m){return(C(n*2-m,n)-C(n*2-m,n-m-1)+mod)%mod;}
int main(){
scanf("%d%d",&n,&mod);
mul[0]=dp[0][0]=1;
for(int i=1;i<MAXM;++i) mul[i]=1ll*mul[i-1]*i%mod;
inv[MAXM-1]=ksm(mul[MAXM-1],mod-2);
for(int i=MAXM-2;~i;--i) inv[i]=1ll*inv[i+1]*(i+1)%mod;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j){
if(i==n-1&&j==n-1)continue;
int left=2*n*n-min(i,j)*n*2-i-j-2;
if(j==i+1)
for(int k=0;k<=n;++k)
dp[i+1][j]=(dp[i+1][j]+1ll*dp[i][j]*C(left-k,n*2-k)%mod*Cat(n,k)%mod)%mod;
else{
dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
if(i==j) dp[i][j+1]=(dp[i][j+1]+dp[i][j])%mod;
else dp[i][j+1]=(dp[i][j+1]+1ll*dp[i][j]*C(left,n*2)%mod*Cat(n,0)%mod)%mod;
}
}
printf("%d\n",dp[n-1][n-1]);
}