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)\) 的次数很好确定

\[\binom{n}{i}\binom{i}{j_0}\binom{n-i}{k_0}\binom{n-i-k_0}{k_1}2^{k_1} \]

其中 \(2^{k_1}\) 表示选取的是 \(a_{2i-1}=i\)\(a_{2i}=i\)

对答案的贡献次数也好计算

\[\binom{j_0+k_0}{j_0}j_0!\dfrac{(2k_0+k_1)!}{2^{k_0}} \]

\(F\) 值相当于上边两个式子相乘。

但是容斥系数却是什么呢?通过打表发现答案为 \((-1)^{j_0+k_1}\) 证明过程由于篇幅较多故放到最后

下面就是平凡的推式子环节:那么答案可以写成

\[\begin{aligned} Ans\times 2^n&=\sum_{i=0}^n\sum_{j_0=0}^{i}\sum_{k_0=0}^{n-i}\sum_{k_1=0}^{n-i-k_0} (-1)^{j_0+k_1}\binom{n}{i}\binom{i}{j_0}\binom{n-i}{k_0}\binom{n-i-k_0}{k_1}2^{k_1}\binom{j_0+k_0}{j_0}j_0!\dfrac{(2k_0+k_1)!}{2^{k_0}}\\ &=\sum_{i=0}^n \sum_{j_0=0}^i\sum_{k_0=0}^{n-i} (-1)^{j_0} \binom{n}{i}\binom{i}{j_0}\binom{n-i}{k_0}j_0!\dfrac{1}{2^{k_0}}\sum_{k_1=0}^{n-i-k_0} (-1)^{k_1} \binom{n-i-k_0}{k_1}(2k_0+k1)! 2^{k_1} \end{aligned} \]

显然 \(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;
}
posted @ 2021-06-03 17:35  siruiyang_sry  阅读(139)  评论(0编辑  收藏  举报