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;
}
博客保留所有权利,谢绝学步园、码迷等不在文首明显处显著标明转载来源的任何个人或组织进行转载!其他文明转载授权且欢迎!