zoukankan      html  css  js  c++  java
  • LOJ6609 无意识的石子堆 加强版 (容斥)

    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;
    }
    
    
    
    
  • 相关阅读:
    【转】shell脚本常用命令
    【转】应该知道的Linux技巧
    背景颜色变成豆沙绿色~
    缓冲区溢出攻击
    恶意代码简介
    atitit.  web组件化原理与设计
    Atitit.vod 视频播放系统 影吧系统的架构图 架构体系 解决方案
    Atitit.跨语言异常转换机制 java c# php到js的异常转换
    atitit.React   优缺点 相比angular react是最靠谱的web ui组件化方案了
    Atiti。流量提升软件设计大纲规划 v1 q45
  • 原文地址:https://www.cnblogs.com/winlere/p/12932814.html
Copyright © 2011-2022 走看看