JSWC2019 小X的咒语
小 X 的咒语
题意
求有多少张有向图满足 点数为 \(n\) ,每个点入度,出度均为 \(2\) ,无重边与自环 ,答案对 \(P\) 取模。
\(1\leq n\leq 500\) ,\(P\) 为奇数。
题解
如果没有无重边与自环的限制那么简单组合数即可解决。暴力 \(dp\) 解决重边与自环问题复杂度太高,考虑容斥。
钦定有 \(a\) 个点有自环,\(b\) 个点有重边,那么答案可以写成 \(\sum_{a,b} (-1)^{a+b} F_{a,b}\) ,其中 \(F_{a,b}\) 表示 \(a,b\) 所构成的贡献。但是 \(F_{a,b}\) 也无法快速计算,因为有些点有重边也有自环,这还要容斥?(晕)
那么考虑一个更暴力的容斥。 为了方便表达先形式化的定义此问题。
设 \(a_{2i-1},a_{2i}\) 表示 \(i\) 号点连出去的两条出边,则需要计算满足要求的序列个数。
- 在 \(a_i\) 序列中 \(1\sim n\) 均出现两次。
- \(a_{2i-1}\neq a_{2i}\) 。
- \(a_{2i-1}\neq i\) 且 \(a_{2i}\neq i\) 。
- \(a_{2i-1}<a_{2i}\)
第四个限制可以先忽略最后将答案除以 \(2^n\) 即可。
设 \((i,j_0,j_2,k_0,k_1,k_2)\) 表示钦定 \(i\) 个点满足 \(a_{2i-1}=a_{2i}\) ,其中有 \(j_0\) 个对三限制没有要求,有 \(j_2\) 个满足 \(a_{2s-1}=s,a_{2s}=s\) 。
形式化的讲:
- \(j_p\) 表示对二限制有要求,其中对于一个点不满足三限制的有 \(p\) 个。可以看出 \(p=1\) 时无意义,则 \(p\in \{0,2\}\) 。
- \(k_p\) 表示对二限制没有要求,其中对于一个点不满足三限制的有 \(p\) 个,其中 \(p\in [0,2]\) 。
- \(i=j_0+j_2\) 。
**事实上 \(k_2\) 也没有用,但是在推容斥系数时可以证明必须存在 \(k_2\) ,详情见下文。 **
设 \(F(i,j_0,j_2,k_0,k_1,k_2)\) 表示 \((i,j_0,j_2,k_0,k_1,k_2)\) 对答案的贡献。
\((i,j_0,j_2,k_0,k_1,k_2)\) 的次数很好确定
其中 \(2^{k_1}\) 表示选取的是 \(a_{2i-1}=i\) 或 \(a_{2i}=i\) 。
对答案的贡献次数也好计算
\(F\) 值相当于上边两个式子相乘。
但是容斥系数却是什么呢?通过打表发现答案为 \((-1)^{j_0+k_1}\) ,证明过程由于篇幅较多故放到最后。
下面就是平凡的推式子环节:那么答案可以写成
显然 \(k_1\) 的只与 \(i,k_0\) 有关,与 \(j_0\) 无关,那么预处理出 \((-1)^{k_1} \binom{n-i-k_0}{k_1}(2k_0+k1)2^{k_1}!\) ,时间复杂度 \(\mathcal O(n^3)\) 。
容斥系数的证明
由于我不太会直观感受该题的容斥系数于是就只能理性证明了(
设五元组 \((j_0,j_1,j_2,k_1,k_2)\) ,其中本题希望当 \(j_0=j_1=j_2=k_1=k_2=0\) 时总贡献次数为 \(1\) ,其中条件下总贡献次数为 \(0\) 。
显然五维是独立的,单独来看。
- \(j_0\rightarrow j_0\)
- \(j_2\rightarrow j_0/j_2/k_1/k_2\)
- \(k_1\rightarrow k_1\)
- \(k_2\rightarrow k_1/k_2\)
其中 \(j_2\rightarrow k_1,k_2\rightarrow k_1\) 的系数为 \(2\) ,因为对于自环会被 \(a_{2i-1}=i/a_{2i}=i\) 计算两次。其他均为 \(1\) 。
- \(j_0\) 的选取方案为 \(\binom{j_0}{j'_0}\) ,若满足 \([j_0=0]\) 的容斥系数只能为 \((-1)^{j'_0}\) 。
- \(j_2\) 的选取方案为 \(\binom{j_2}{k'_1}2^{k'_1}\binom{j_2-k'_1}{j'_0,j'_2,k'_2}\) ,该式子可以看成 \((2+1+1+1+1)^{j_2}\) ,其中每一项分别表示 \((k_1,j_0,j_2,k_2,1)\) ,那么容斥系数可以为 \((-1)^{k'_1}(-1)^{j'_0/j_2/k_2}\) 或 \((-1)^{j_0+j_2+k_2}\) 。
- \(k_1\) 的选取方案为 \(\binom{k_1}{k'_1}\) ,若满足 \([k_1=0]\) 的容斥系数只能为 \((-1)^{k'_1}\) 。
- \(k_2\) 的选取方案为 \(\binom{k_2}{k'_1} 2^{k'_1} \binom{k_2-k'_1}{k'_2}\) ,该式子可以看成 \((2+1+1)^{k_2}\) ,其中每一项分别表示 \((k_1,k_2,1)\) ,那么容斥系数可以为 \((-1)^{k'_1}\) 或 \((-2)^{k_2}\) 。
所以仅需要选取 \((-1)^{j_0+k_1}\) 作为容斥系数即可满足要求。
这也说明了若去掉 \(k_2\) 是不存在容斥系数可以满足要求的 。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<queue>
#include<vector>
#define int long long
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;
inline int read(){
int f=1,ans=0; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
return f*ans;
}
const int MAXN=511;
int pw[MAXN],ipw[MAXN],mod,inv2,N,Ans,binom[MAXN][MAXN],F[MAXN][MAXN],fac[MAXN<<1];
int Cnt(int i,int j0,int j1,int j2,int k0,int k1,int k2){
return binom[N][i]*binom[i][j1]%mod*binom[i-j1][j2]%mod*binom[N-i][k1]%mod*binom[N-i-k1][k2]%mod*pw[j1]%mod*pw[k1]%mod;
}
int W(int i,int j0,int j1,int j2,int k0,int k1,int k2){
return binom[j0+k0][j0]*fac[j0]%mod*fac[2*k0+k1]%mod*ipw[k0]%mod;
}
int CC(int i,int j0,int k0,int k1){
return binom[N][i]*binom[i][j0]%mod*binom[N-i][k0]%mod*binom[N-i-k0][k1]%mod*pw[k1]%mod;
}
int WW(int i,int j0,int k0,int k1){
return binom[j0+k0][j0]*fac[j0]%mod*fac[2*k0+k1]%mod*ipw[k0]%mod;
}
signed main(){
read();
N=read(),mod=read(); inv2=(mod+1)/2;
pw[0]=ipw[0]=1; for(int i=1;i<=N;i++) pw[i]=pw[i-1]*2%mod,ipw[i]=ipw[i-1]*inv2%mod;
fac[0]=1; for(int i=1;i<=2*N;i++) fac[i]=fac[i-1]*i%mod;
binom[0][0]=1; for(int i=1;i<=N;i++){binom[i][0]=1;for(int j=1;j<=i;j++) binom[i][j]=(binom[i-1][j-1]+binom[i-1][j])%mod;}
for(int i=0;i<=N;i++) for(int k0=0;k0<=N-i;k0++) for(int k1=0;k1<=N-k0-i;k1++) F[i][k0]+=((k1&1)?-1:1)*binom[N-i-k0][k1]%mod*fac[2*k0+k1]%mod*pw[k1]%mod,F[i][k0]%=mod;
for(int i=0;i<=N;i++) for(int j0=0;j0<=i;j0++) for(int k0=0;k0<=N-i;k0++){
int bas=binom[N][i]*binom[i][j0]%mod*binom[N-i][k0]%mod*binom[j0+k0][j0]%mod*fac[j0]%mod*ipw[k0]%mod;
int res=F[i][k0];
if(j0&1) res=-res;
Ans+=bas*res%mod,Ans%=mod;
}
Ans=(Ans+mod)%mod;
printf("%lld\n",Ans*ipw[N]%mod);
return 0;
}