LOJ6609 无意识的石子堆 加强版 (容斥)

LOJ6609 无意识的石子堆 加强版 (容斥)

永远也不会做容斥的题

枚举有\(i\)列放了两个,先把这些列选出来注意到此时有\(i\)列需要两个棋子,\(2\times n-2\times i\)需要一个棋子。设\(a_i\)表示\(i\)情况的方案数。

答案就是

\[\sum_i {m\choose i}{m-i \choose 2n-2 i} a_i \]

考虑如何计算\(a_i\)。现在问题变成有\(n\)种不同颜色球,每种颜色恰好\(2\)个,现在要放到\(i+2n-2i\)个不相同的盒子中,其中\(i\)个盒子要放\(2\)个(无序),\(2n-2i\)个盒子中要放\(1\)个。一个盒子不能放两个相同颜色的球,问方案数。

首先是把所有的无序转为有序,把盒子的空位看做有序,每种颜色的两个球看做有序,最终方案除以\(2^{n+i}\)即可。

此外考虑一些限制,不好满足的限制是一个盒子不能放两个相同颜色的球的限制,考虑对这个进行容斥。

所以

\[a_i={1\over 2^{n+i}}\sum_j {n\choose j} {i\choose j} j! (-1)^j 2^j (2n-2j)! \]

选出\(j\)个颜色/选出\(j\)个盒子/有序选盒子哦~/容斥系数/盒子的位置是有序的/剩下有\(2n-2j\)个球放到\(2n-2j\)个盒子就是阶乘

此外要处理一个下降幂,递推即可。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<assert.h>

using namespace std;  typedef long long ll;  
inline ll qr(){
	ll ret=0,f=0,c=getchar();
	while(!isdigit(c)) f|=c==45,c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return f?-ret:ret;
}
const int mod=943718401;
int MOD(const int&x){return x>=mod?x-mod:x;}
int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
int MOD(const vector<ll>&ve){int ret=1;for(auto t:ve)ret=MOD(ret,t);return ret;}
typedef vector<int> poly;

int ksm(const int&ba,const int&p){
	int ret=1;
	for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
		if(t&1) ret=MOD(ret,b);
	return ret;
}
const int g=7;
const int gi=ksm(g,mod-2);

void NTT(poly&a,int tag){
	int len=a.size();
	static int r[1<<22];
	for(int t=1;t<len;++t)
		if((r[t]=r[t>>1]>>1|(t&1?len>>1:0))>t)
			swap(a[t],a[r[t]]);	
	for(int t=1,s=tag==1?g:gi,wn;t<len;t<<=1){
		wn=ksm(s,(mod-1)/(t<<1));
		for(int i=0;i<len;i+=t<<1)
			for(int j=0,w=1,p;j<t;++j,w=MOD(w,wn))
				p=MOD(w,a[i+j+t]),a[i+j+t]=MOD(a[i+j]-p+mod),a[i+j]=MOD(a[i+j]+p);
	}
	if(tag!=1)
		for(int t=0,i=mod-(mod-1)/len;t<len;++t)
			a[t]=MOD(a[t],i);
}

poly operator * (poly a,poly b){
	int t1=a.size()+b.size()-1,len=1;
	while(len<t1) len<<=1;
	a.resize(len); b.resize(len);
	NTT(a,1); NTT(b,1);
	for(int t=0;t<len;++t) a[t]=MOD(a[t],b[t]);
	NTT(a,0); return a.resize(t1),a;
}
const int maxn=4e6+5;
int jc[maxn],inv[maxn],mi[maxn],ch[maxn];
ll n,m;

void pre(int n){
	jc[0]=inv[0]=1;
	for(int t=1;t<=n;++t) jc[t]=MOD(jc[t-1],t);
	inv[n]=ksm(jc[n],mod-2);
	for(int t=n-1;t;--t) inv[t]=MOD(inv[t+1],t+1);
}

int c(const int&n,const int&m){
	return n<m||n<0?0:MOD({jc[n],inv[m],inv[n-m]});
}

void pre2(){
	m%=mod;
	mi[0]=1;
	for(int t=1;t<=n<<1;++t)
		mi[t]=MOD(mi[t-1],m-t+1);
	ch[n]=1;
	for(int t=n-1;~t;--t)
		ch[t]=MOD({ch[t+1],m-t,m+t-2*n+1,ksm(MOD(2*n-2*t-1,2*n-2*t),mod-2)});
}

int main(){
	n=qr(),m=qr();
	pre(4e6);
	poly a,b(n+1),d(inv,inv+n+1);
	for(int t=0;t<=n;++t) b[t]=MOD({::c(n,t),ksm(2,t),t&1?mod-1:1,jc[2*n-2*t]});
	a=b*d;
	for(int t=0;t<=n;++t) a[t]=MOD({a[t],ksm((mod+1)>>1,n+t),jc[t]});
	int ans=0;
	if(m>2*n){
		pre2();
		for(int t=0;t<=n;++t)
			ans=MOD(ans+MOD({mi[t],inv[t],ch[t],a[t]}));
	}
	else for(int t=0;t<=n;++t) ans=MOD(ans+MOD({c(m,t),c(m-t,2*n-2*t),a[t]}));
	printf("%d\n",ans);
	return 0;
}



posted @ 2020-05-21 19:25  谁是鸽王  阅读(287)  评论(0编辑  收藏  举报