LOJ6609 无意识的石子堆 加强版 (容斥)
永远也不会做容斥的题
枚举有(i)列放了两个,先把这些列选出来注意到此时有(i)列需要两个棋子,(2 imes n-2 imes i)需要一个棋子。设(a_i)表示(i)情况的方案数。
答案就是
[sum_i {mchoose 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={1over 2^{n+i}}sum_j {nchoose j} {ichoose 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
",ans);
return 0;
}