• [bzoj3027][Ceoi2004]Sweet【生成函数】【组合数】


    [题目描述]

    3027: [Ceoi2004]Sweet

    Time Limit: 1 Sec  Memory Limit: 128 MB
    Submit: 137  Solved: 68
    [Submit][Status][Discuss]

    Description

    John得到了n罐糖果。不同的糖果罐,糖果的种类不同(即同一个糖果罐里的糖果种类是相同的,不同的糖果罐里的糖果的种类是不同的)。第i个糖果罐里有 mi个糖果。John决定吃掉一些糖果,他想吃掉至少a个糖果,但不超过b个。问题是John 无法确定吃多少个糖果和每种糖果各吃几个。有多少种方法可以做这件事呢?  
      
     

    Input


    从标准输入读入每罐糖果的数量,整数a到b 
     
    John能够选择的吃掉糖果的方法数(满足以上条件)  
     

    Output


     
    把结果输出到标准输出(把答案模 2004 输出) 

    1<=N<=10,0<=a<=b<=10^7,0<=Mi<=10^6

    Sample Input

    2 1 3
    3
    5

    Sample Output

    9

    HINT

    (1,0),(2,0),(3,0),(0,1),(0,2),(0,3),(1,1),(1,2),(2,1) 

    Source

    [题解]
    用生成函数将答案表示出来,函数为:
    1/((1-x)^n)*(1-x^(m1+1))*(1-x^(m2+1))*...*(1-x^(mn+1))
    第2项到第(m+1)项乘出的非零项不会超过2^n,暴力枚举计算贡献;
    第一项为一串组合数的前缀和,设当前枚举的后面的项的次数为k
    ans=C(n-1,n-1)+C(n,n-1)+..+C(n+lim-k-1,n-1)
    =C(n,n)+C(n,n-1)+..+C(n+lim+k-1,n-1) 
    因为C(n,n)+C(n,n-1)=C(n+1,n),所以ans=C(n+lim+k,n);
    在取模时C(n,m)=n!/(m!*(n-m)!)%P,可以先将P*m!最后将答案除m!
    证明:设C(n,m)%(P*m!)=A 即A+k*Pm!=n!/(m-n)!
    A/m!+k*P=n!/(m-n)!m! 因为k*P∈Z,C(n,m)∈Z 所以A/m!∈Z
    因此 C(n,m)%P=A/m!
    [代码]
    /* --------------
        user Vanisher
        problem bzoj-3027
    ----------------*/
    # include <bits/stdc++.h>
    # define 	ll 		long long
    # define 	N 		11
    # define 	M 		10000100
    # define 	P 		2004
    using namespace std;
    int m[N],a,b,ans,n;
    int read(){
    	int tmp=0, fh=1; char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    	return tmp*fh;
    }
    int C(int n, int m){
        if (n<m) return 0;
        ll t=1; ll p1=1;
        for (int i=1;i<=m;i++) p1=p1*i;
        ll mod2=(ll)P*p1;
        for (int i=n-m+1;i<=n;i++) t=(ll)t*i%mod2;
        return (t/p1)%P;
    }
    void check(int k, int lim, int tag){
    	//int l=C(n-1,n-1), r=C(n+lim-k-1,n-1); --> C(n+lim-k-1,n);
    	ans=(ans+C(n+lim-k,n)*tag+P)%P;
    }
    void dfs(int k, int now, int lim, int tag){
    	if (now>lim) return;
    	if (k>n) {
    		check(now,lim,tag);
    		return;
    	}
    	dfs(k+1,now,lim,tag);
    	dfs(k+1,now+m[k]+1,lim,tag*(-1));
    }
    int main(){
    	n=read(), a=read(), b=read();
    	for (int i=1; i<=n; i++) m[i]=read();
    	dfs(1,0,a-1,-1); dfs(1,0,b,1);
    	cout<<ans<<endl; 
    	return 0;
    }
    


  • 相关阅读:
    2407: C语言习题 整数转换成字符串
    2484: 字母图形
    1658: Easier Done Than Said?
    Easier Done Than Said?(应用到的知识)
    1653: Champion of the Swordsmanship
    1614: 五位以内的对称素数
    1612: 最小公倍数
    $ vs $()
    数学
    计算机科学与技术课程
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136050.html
Copyright © 2020-2023  润新知