组合数学PA2019数图 / Grafy PA 2019 Final
WZH [PA 2019 Final] 数图 / Grafy
我不会容斥。
求出 n 个节点的满足以下条件的有标号有向简单图的数量:
- ∀1≤i≤n,degout(i)=degin(i)=2。
这里,degout(u) 指节点 u 的出度,degin(u) 指节点 u 的入度。
你只需要输出答案对给定大素数 p 取模后的结果。
n≤500。
首先,将图转化为一个邻链表存储。即改为计数 b[0⋯2n−1],其中 b2i 和 b2i+1 表示 i 的两条邻边。
考虑到顺序问题过于麻烦,考虑忽略顺序,即忽略一个点的出边和入边的顺序。对于 b 数组相当于改为计数一个 a 数组,满足 a 为一个 0∼2n−1 的排列。并满足 bi=⌊2ai⌋。
接着,把所有限制写下来:
- b2i=b2i+1。
- b2i=i。
- b2i+1=i。
那么相当于要计数:
a∑i∏[b2i=b2i+1][b2i=i][b2i+1=i]
将里面的展开:
[b2i=b2i+1][b2i=i][b2i+1=i]=(1−[b2i=b2i+1])(1−[b2i=i])(1−[b2i+1=i])=1−[b2i=b2i+1]−[b2i=i]−[b2i+1=i]+2[b2i=i][b2i+1=i]
那么就要计数:
a∑i∏1−[b2i=b2i+1]−[b2i=i]−[b2i+1=i]+2[b2i=i][b2i+1=i]
考虑钦定 i 项个满足 [b2i=i],j 个项满足 [b2i+1=i],k 个项满足 [b2i=b2i+1],t 个项满足 [b2i=i][b2i+1=i] 。则有 w=n−i−j−k−t 个项任意选择。
那么首先,我们要钦定这些项的位置,有 (i,j,k,t,wn) 种方案。
然后,序列 a 任意排序。但是由于有 i+j 个项中 b2i 和 b2i+1 有一个被强制钦定为 i,k+t 有两个都被强制钦定。于是总共只有 n+w−k−t 个项任选,即 (n+w−k−t)! 中方案。
紧接着,我们应该看 i,j,k,t,w 内部对应的方案。
Case #1: i,j
由于 i 类的项被强制钦定 b2i=i,即 a2i=2i/2i+1,故每一个项有两种方案。由于 [b2i=i] 的容斥系数为 −1,故每一个项会产生 −2 的贡献,一共就是 (−2)i 。
j 同理,为 (−2)j 。
Case #2: t
t 类最简单,一共只有 (a2i,a2i+1)=(2i,2i+1)/(2i+1,2i) 两种方案,由于容斥系数为 2 ,故有 22t 的贡献。
Case #3: k
这种最麻烦。由于只钦定了 b2i=b2i+1,因此 b2i 必然没有出现在被 i,j,t 类限制的项中,于是只能出现在 k,w 类出现的项,于是有 (kk+w)k! 中钦定方案。然后类似的,像 t 类,每一个有 −2 的贡献。于是总共有 (kk+w)k!(−2)k 中方案。
Case #4: p
i+j+k+t+w=n∑(i,j,k,t,wn)(n+w−k−t)!(−2)i(−2)j(kk+w)k!(−2)k22t
直接算复杂度 O(n4)
由于 i,j 很想,不妨令 p=i+j 代换。
那么此时的 p 类的贡献就像 i 和 j 一样,有 b2i=2i,b2i=2i+1,b2i+1=2i,b2i+1=2i+1 四种方案,同时容斥系数为 −1 ,于是贡献为 (−1)p22p 。
那么此时的答案:
p+k+t+w=n∑(p,k,t,wn)(n+w−k−t)!(−1)p+k2k+2p+2t(kk+w)k!
直接枚举可以做到 O(n3) 。
容斥好题!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <bits/stdc++.h> using namespace std; const int N=510; int n,P,fac[N<<1],inv[N<<1],pw[N<<1]; int kpow(int a,int b){ int t=1; while(b){ if(b&1)t=1ll*t*a%P; a=1ll*a*a%P; b>>=1; } return t; } void init(int n){ pw[0]=1; for(int i=1;i<=n;i++)pw[i]=2ll*pw[i-1]%P; fac[0]=1; for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%P; inv[n]=kpow(fac[n],P-2); for(int i=n-1;~i;i--)inv[i]=1ll*inv[i+1]*(i+1)%P; } int Inv(int a){return a<=(n<<1)?kpow(a,P-2):(P-1ll*(P/a)*Inv(P%a)%P)%P;} int main(){ scanf("%d%d",&n,&P); init(n<<1); int ans=0; for(int p=0;p<=n;p++)for(int k=0;p+k<=n;k++)for(int t=0;p+k+t<=n;t++){ int w=n-p-k-t; int res=1ll*fac[n]*inv[p]%P*inv[k]%P*inv[t]%P*inv[w]%P *pw[k+2*p+2*t]%P*fac[k+w]%P*inv[k]%P*inv[w]%P* fac[n+w-k-t]%P*fac[k]%P; (ans+=((p+k&1)?P-res:res))%=P; } printf("%lld",1ll*ans*Inv(pw[2*n])%P); return 0; }
|