JSWC2019 小X的咒语

小 X 的咒语

题意

求有多少张有向图满足 点数为 (n) ,每个点入度,出度均为 (2) ,无重边与自环 ,答案对 (P) 取模。

(1leq nleq 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) 序列中 (1sim n) 均出现两次。
  • (a_{2i-1} eq a_{2i})
  • (a_{2i-1} eq i)(a_{2i} eq 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) 时无意义,则 (pin {0,2})
  • (k_p) 表示对二限制没有要求,其中对于一个点不满足三限制的有 (p) 个,其中 (pin [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)) 的次数很好确定

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

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

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

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

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

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

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

[egin{aligned} Ans imes 2^n&=sum_{i=0}^nsum_{j_0=0}^{i}sum_{k_0=0}^{n-i}sum_{k_1=0}^{n-i-k_0} (-1)^{j_0+k_1}inom{n}{i}inom{i}{j_0}inom{n-i}{k_0}inom{n-i-k_0}{k_1}2^{k_1}inom{j_0+k_0}{j_0}j_0!dfrac{(2k_0+k_1)!}{2^{k_0}}\ &=sum_{i=0}^n sum_{j_0=0}^isum_{k_0=0}^{n-i} (-1)^{j_0} inom{n}{i}inom{i}{j_0}inom{n-i}{k_0}j_0!dfrac{1}{2^{k_0}}sum_{k_1=0}^{n-i-k_0} (-1)^{k_1} inom{n-i-k_0}{k_1}(2k_0+k1)! 2^{k_1} end{aligned} ]

显然 (k_1) 的只与 (i,k_0) 有关,与 (j_0) 无关,那么预处理出 ((-1)^{k_1} inom{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 ightarrow j_0)
  • (j_2 ightarrow j_0/j_2/k_1/k_2)
  • (k_1 ightarrow k_1)
  • (k_2 ightarrow k_1/k_2)

其中 (j_2 ightarrow k_1,k_2 ightarrow k_1) 的系数为 (2) ,因为对于自环会被 (a_{2i-1}=i/a_{2i}=i) 计算两次。其他均为 (1)

  • (j_0) 的选取方案为 (inom{j_0}{j'_0}) ,若满足 ([j_0=0]) 的容斥系数只能为 ((-1)^{j'_0})
  • (j_2) 的选取方案为 (inom{j_2}{k'_1}2^{k'_1}inom{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) 的选取方案为 (inom{k_1}{k'_1}) ,若满足 ([k_1=0]) 的容斥系数只能为 ((-1)^{k'_1})
  • (k_2) 的选取方案为 (inom{k_2}{k'_1} 2^{k'_1} inom{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
",Ans*ipw[N]%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/si-rui-yang/p/14846200.html