• [洛谷P6078] CEOI2004 candy


    问题描述

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

    输入格式

    输入共 n+1 行:

    第一行读入 n,a,b。

    接下来 n 行,一行一个数,代表 mi。

    输出格式

    仅一行,表示 John 能够选择的满足以上条件的吃掉糖果的方法数,答案对 2004 取模。

    样例输入

    2 1 3
    3
    5

    样例输出

    9

    数据范围

    (1le nle 10,0leq a leq b leq 10^7,0 leq m_{i} leq 10^6)

    解析

    比较明显的,这是一道生成函数题。

    我们先构造出生成函数,为数列 {1,1,1,...1} 的生成函数。转化一下,问题就是求一下多项式的a次项系数到b次项系数的和:

    [prod_{i=1}^{n}(1+x+x^2+...+x^{m_i}) ]

    这样并不好求,时间的限制不允许我们使用NTT之类的算法。我们要将之前的生成函数转化一下:

    [egin{align} F(x)&=sum_{i=0}^mf_ix^i\ &=sum_{i=0}^m(f_{i-1}+[n=0])x^i\ &=(sum_{i=0}^m f_{i-1}x^i)+1\ &=x(F(x)-x^m)+1 end{align} ]

    因此,我们可以得到

    [F(x)=frac{1-x^{m_i+1}}{1-x} ]

    将分子和分母分开讨论。对于分子,同样的道理,我们知道 (frac{1}{1-x}=1+x+x^2+...),那么 ((frac{1}{x-1})^n) 对多项式 (i) 次项的系数的贡献为 (C_{i+n-1}^{n-1})(由插板法可以比较简单地得到这个结论)。对于分母,由于n很小,我们可以直接搜索得到每一次项的系数,记为 (a_i)

    回到原问题上面来。设(S(i))表示前 (i) 次项系数和。不难得到,我们有

    [S(x)=sum_{i=0}^{x}a_isum_{j=0}^{x-i}C_{j+n-1}^{n-1} ]

    画一画杨辉三角可以发现,(sum_{j=0}^{x-i}C_{j+n-1}^{n-1}) 是三角上连续一列的和,即 (C_{n+x-j}^x) 。然后就可以一边搜索分子的各项系数一边统计答案了。

    代码

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define N 12
    using namespace std;
    const int mod=2004;
    int n,a,b,i,m[N],fac,ans;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    int C(int n,int m)
    {
    	if(n<m) return 0;
    	int ans=1;
    	for(int i=n-m+1;i<=n;i++) ans=ans*i%(fac*mod);
    	return (ans/fac)%mod;
    }
    void dfs(int x,int a,int b,int c)
    {
    	if(x==n+1){
    		int tmp=C(n+c-a,n);
    		if(b<0) ans=(ans-tmp+mod)%mod;
    		else if(b>0) ans=(ans+tmp)%mod;
    		return;
    	}
    	dfs(x+1,a,b,c);
    	dfs(x+1,a+m[x]+1,-b,c);
    }
    int cal(int a)
    {
    	ans=0;
    	dfs(1,0,1,a);
    	return ans;
    }
    signed main()
    {
    	n=read();a=read();b=read();
    	for(i=1;i<=n;i++) m[i]=read();
    	for(i=fac=1;i<=n;i++) fac*=i;
    	printf("%lld
    ",(cal(b)-cal(a-1)+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    实战分享 | 你知道这个死锁是怎么产生的吗?
    HDU 3016 线段树区间更新+spfa
    POJ 2828 线段树(想法)
    POJ 2184 01背包+负数处理
    HDU 2955 01背包(思维)
    HDU 1171 背包
    HDU 1561 树形DP入门
    POJ 3694 tarjan 桥+lca
    POJ 2446 最小点覆盖
    POJ 2226 最小点覆盖(经典建图)
  • 原文地址:https://www.cnblogs.com/LSlzf/p/13379136.html
Copyright © 2020-2023  润新知