zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Search Insert Position——二分法
    Majority Element——算法课上的一道题(经典)
    Max Points on a Line——数学&&Map
    Valid Number——分情况讨论最经典的题(没细看)——这题必须静下心来好好看看
    Sqrt(x)——二分法,防越界
    Ubuntu系统---NVIDIA 驱动安装
    系统---《windows + ubuntu双系统》
    VS---《在VS2010中 使用C++创建和使用DLL》(003)
    VS---《在VS2010中 使用C++创建和使用DLL》(002)
    图像处理---《在图片上打印文字 windows+GDI+TrueType字体》
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/14846200.html
Copyright © 2011-2022 走看看