zoukankan      html  css  js  c++  java
  • ●BZOJ 1272 [BeiJingWc2008]Gate Of Babylon

    题链:

    http://www.lydsy.com/JudgeOnline/problem.php?id=1272

    题解:

    容斥,Lucas定理
    本题的容斥考虑类似
    [BZOJ 1042 [HAOI2008]硬币购物]
    ANS = 至少 0 种超级神器超过数量限制的方案数
                  -至少 1 种超级神器超过数量限制的方案数
                 +至少 2 种超级神器超过数量限制的方案数
                  -...+...
    那么就下来就需要解决两个问题:
    1).如果随便选的话,如何求出方案数?
    假设需要从 N 种任意宝具里选出不超过 m 个,求法如下:
    先考虑必须选 m 个:
    这是一个典型的组合问题:
    即要把 m 个相同的小球放到 N 个盒子里,且盒子可以为空,求方案数。
    则方案数为 ${C}_{N+m-1}^{N-1}$。
    怎么理解呢? 可以叫做 "插隔板法" 吧。
    如果现在有 N+m-1 个位置,我们可以在 N-1 个位置放隔板,
    并且令相邻的两个隔板(把首尾也看作另外2个隔板)中间的空余位置放小球。
    (相邻的两个隔板之间共有 N 个间隙,所以可以把每个间隙依次看做一个盒子。)
    则任意一种插隔板的方法都对应一种把小球放入盒子的方法。
    所以,方案数为
    ${C}_{N+m-1}^{N-1}$

    但是这是必须选m个的方案数。
    所以需要把下面东西加起来才是选的物品不超过 m个 的方案数

    ${W=}{C}_{N-1}^{N-1}+{C}_{N}^{N-1}+{C}_{N+1}^{N-1}+cdots+{C}_{N+m-1}^{N-1}$
    然后需要化简这个式子:由一个小性质 : ${C}_{i-1}^{j}+{C}_{i-1}^{j-1}={C}_{i}^{j}$

    所以 ${W} = {W} + {C}_{N-1}^{N} – {C}_{N-1}^{N}$
    然后就可以从前面一直结合到末尾,得到
    ${W} = {C}_{N+m}^{N} – {C}_{N-1}^{N}$,但是 ${C}_{N-1}^{N}={0}$
    所以  ${W} = {C}_{N+m}^{N}$
    然后第一个问题就这么愉快地解决了。
    (哦,还有,这个组合数的 N,M都太大了但模数 P 很小,需要用到Lucas定理,不会的快百度百科一下!)
    2).如何求出那些用于容斥的方案数呢?
    考虑将一些超级神器强制超过使用其限制的个数(设超出的总个数为 k),
    那么在接下来,在 N 种宝具里任意选出不超过剩下的 m=M-k 个物品的方案都为非法方案,
    而这个方案数就可以用第一个问题的解决方法去求解。

    所以具体做法就是枚举一个集合 S表示该集合里的超级神器会被强制使用超过使用其限制的个数,
    然后求出该情况下的方案数后,对应着本文开篇的容斥式子去加加减减就好啦。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define _ % P
    #define filein(x) freopen(#x".in","r",stdin);
    #define fileout(x) freopen(#x".out","w",stdout);
    using namespace std;
    int B[100500],h[100500],k[100500],fac[100500],inv[100500];
    int N,T,M,P,ANS;
    int pow(int a,int b){
    	int ret=1;
    	while(b){
    		if(b&1) ret=(1ll*ret*a)_;
    		a=(1ll*a*a)_; b>>=1;
    	}
    	return ret;
    }
    int C(int n,int m){
    	if(n<m) return 0;
    	return 1ll*fac[n]*inv[m]_*inv[n-m]_;
    }
    int Lucas(int n,int m){
    	int ret=1;
    	while(m){
    		ret=(1ll*ret*C(n%P,m%P))_;
    		n/=P; m/=P;
    	}
    	return ret;
    }
    int main()
    {
    	scanf("%d%d%d%d",&N,&T,&M,&P); fac[0]=inv[0]=1;
    	for(int i=1;i<P;i++) fac[i]=(1ll*fac[i-1]*i)_;
    	inv[P-1]=pow(fac[P-1],P-2);
    	for(int i=P-2;i;i--) inv[i]=(1ll*inv[i+1]*(i+1))_;
    	for(int i=1;i<=T;i++) scanf("%d",&B[1<<(i-1)]);
    	for(int s=1,q;q=(s&-s),s<(1<<T);s++) 
    		k[s]=1ll*k[s^q]+B[q]+1,
    		h[s]=h[s^q]+1;
    	for(int s=0,m,tmp;s<(1<<T);s++){
    		m=M-k[s]; if(m<0) continue;
    		tmp=Lucas(N+m,N);
    		if(h[s]&1) tmp=(-1ll*tmp+P)_;
    		ANS=((1ll*ANS+tmp)_+P)_;
    	}
    	printf("%d",ANS);
    	return 0;
    }
    

  • 相关阅读:
    【转】查看java类是从哪个包加载
    把本地代码同步到github
    【转】Sublime Text3注册码(可用)
    python网络编程学习笔记(二)
    python网络编程学习笔记(一)
    Linux学习笔记 第五章Linux首次登陆与在线求助 man page
    linux c 笔记 网络编程(三)..套接字数据传输
    linux c 笔记 网络编程(二)
    linux c 笔记 网络编程(一)
    互斥锁 笔记
  • 原文地址:https://www.cnblogs.com/zj75211/p/8040122.html
Copyright © 2011-2022 走看看