牛客生成函数I
简单学了点生成函数。核心在于把多项式的指数看作个数,则多项式系数就是对应个数的方案数。
A. 背包
题意:
有8种物品,每种物品选取个数限制如下:
- 肥宅快乐水:最多1个(0或1)
- 鸡腿:最多1个(0或1)
- 鸡翅:必须偶数个(0,2,4,...)
- 鸡块:必须4的倍数个(0,4,8,...)
- 鸡汤:必须奇数个(1,3,5,...),不能为0
- 鸡蛋:必须3的倍数个(0,3,6,...)
- 大盘鸡:最多2个(0,1,2)
- 啤酒鸡:最多3个(0,1,2,3)
求选取总数为 \(N\) 个物品的方案数,对 \(10^9+7\) 取模。\(N \le 10^{18}\)。
思路:
写出每种物品的生成函数并相乘:
- 肥宅快乐水:\(1+x\)
- 鸡腿:\(1+x\)
- 鸡翅:\(1+x^2+x^4+\cdots = \frac{1}{1-x^2}\)
- 鸡块:\(1+x^4+x^8+\cdots =\frac{1}{1-x^4}\)
- 鸡汤:\(x+x^3+x^5+\cdots = \frac{x}{1-x^2}\)
- 鸡蛋:\(1+x^3+x^6+\cdots =\frac{1}{1-x^3}\)
- 大盘鸡:\(1+x+x^2 = \frac{1-x^3}{1-x}\)
- 啤酒鸡:\(1+x+x^2+x^3 = \frac{1-x^4}{1-x}\)
总生成函数:
约去 \((1-x^3)\) 和 \((1-x^4)\),并利用 \(1-x^2=(1-x)(1+x)\) 化简:
而 \(\displaystyle\frac{1}{(1-x)^4}=\sum_{n\ge 0}\binom{n+3}{3}x^n\),乘以 \(x\) 后 \(x^N\) 系数为 \(\binom{N+2}{3}\)。
所以答案为 \(\displaystyle \frac{N(N+1)(N+2)}{6} \bmod (10^9+7)\)。使用快速幂求6的逆元即可。
代码
点击查看代码
void solve() {
init();
int n,m;
cin>>n;
n--;
cout<<(n+3)%mod*((n+2)%mod)%mod*((n+1)%mod)%mod*qpow(6,mod-2)%mod<<endl;
}
B. Devu and Flowers
题意:
给定 \(n\) 个盒子,第 \(i\) 个盒子中有 \(f_i\) 朵同色花,不同盒子颜色互不相同。求选出恰好 \(s\) 朵花的方案数(同一盒子内选出的花数量不同即视为不同方案),答案对 \(10^9+7\) 取模。\(n\le 20\),\(s\le 10^{14}\),\(f_i\le 10^{12}\)。
思路:
把生成函数相乘:\(\displaystyle\prod_{i=1}^{n}\frac{1-x^{f_i+1}}{1-x}=\displaystyle\frac{1}{(1-x)^n}\prod_{i=1}^{n}(1-x^{f_i+1})\)
容斥,\(\displaystyle\prod_{i=1}^{n}(1-x^{f_i+1})=\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}x^{\sum_{i\in S}(f_i+1)}\)
\(\displaystyle\frac{1}{(1-x)^n}=\sum_{i=0}^{\infty}\binom{i+n-1}{n-1}x^i\)
令 \(\displaystyle\prod_{i=1}^{n}(1-x^{f_i+1})=\sum_{i=0}^{\infty}A_ix^i\),\(\displaystyle\frac{1}{(1-x)^n}=\sum_{i=0}^{\infty}B_ix^i\).
两式子相乘,\((\displaystyle\sum_{i=0}^{\infty}A_ix^i)(\sum_{i=0}^{\infty}B_ix^i)=\sum_{N=0}^{\infty}(\sum_{i+j=N}A_iB_j)x^N\)
\(\displaystyle\sum_{i+j=N}A_iB_j=\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}B_{N-\sum_{i\in S}(f_i+1)}=\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}\binom{N-\sum_{i\in S}(f_i+1)+n-1}{n-1}\)
最后枚举集合S计数即可。
代码
点击查看代码
int C(int n,int m){
int ans=1;
for(int i=0;i<m;i++){
ans*=(n-i)%mod;
ans%=mod;
}
ans*=inv[m];
ans%=mod;
return ans;
}
int b[N];
int a[N],s[N],S[N];
void solve() {
init();
int n,s;
cin>>n>>s;
for(int i=0;i<n;i++){
cin>>a[i];
a[i]++;
}
int ans=0;
for(int i=0;i<(1ll<<n);i++){
int sum=0;
int len=0;
for(int j=0;j<n;j++){
if(i>>j&1){
len++;
sum+=a[j];
}
}
if(sum<=s){
if(len%2){
ans+=mod-C(s-sum+n-1,n-1);
}else{
ans+=C(s-sum+n-1,n-1);
}
ans%=mod;
}
}
cout<<ans<<endl;
}
Sweets
题意
有 \(n\) 种糖果,第 \(i\) 种糖果有 \(m_i\) 颗。John 要吃掉总数在 \([a,b]\) 之间的糖果,每种糖果选的数量不能超过 \(m_i\)。求有多少种不同的选择方案,答案模 \(2004\)。
思路
与上题一样,先求出生成函数,过程略去:
\(\displaystyle\sum_{N=0}^{\infty}(\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}\binom{N-\sum_{i\in S}(m_i+1)+n-1}{n-1})x^N\)
我们需要求该生成函数的多项式系数的区间和,可以等价成前缀和。将生成函数再乘一个\(\displaystyle\sum_{N=0}^{\infty}x^N\)即可得到多项式系数的前缀和。
一种简单的方法是直接从生成函数的形式看:\(\displaystyle\frac{1}{(1-x)^{n+1}}\prod_{i=1}^{n}(1-x^{f_i+1})\),与原生成函数的区别只有\((1-x)\)的指数不同,直接把式子中组合数的\(n\)替换成\(n+1\)即可。
另一种方法需要运用朱世杰恒等式:
\((\displaystyle\sum_{N=0}^{\infty}(\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}\binom{N-\sum_{i\in S}(m_i+1)+n-1}{n-1})x^N)(\displaystyle\sum_{N=0}^{\infty}x^N)\)
\(=\displaystyle\sum_{N=0}^{\infty}(\sum_{x+y=N}(1\cdot\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}\binom{x-\sum_{i\in S}(m_i+1)+n-1}{n-1}))x^N\)
\(=\displaystyle\sum_{N=0}^{\infty}(\sum_{x=0}^N(1\cdot\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}\binom{x-\sum_{i\in S}(m_i+1)+n-1}{n-1}))x^N\)
\(=\displaystyle\sum_{N=0}^{\infty}((1\cdot\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}(\sum_{x=0}^N\binom{x-\sum_{i\in S}(m_i+1)+n-1}{n-1})))x^N\)
\(=\displaystyle\sum_{N=0}^{\infty}((1\cdot\sum_{S\subseteq\{1,2,...,n\}}(-1)^{|S|}\binom{x-\sum_{i\in S}(m_i+1)+n}{n}))x^N\)
然后枚举集合S计数即可,注意模数是2004,不能直接用逆元。
代码
点击查看代码
int A[N],B[N];
int C(int n,int m){
int ans=1;
for(int i=0;i<m;i++){
A[i]=(n-i);
B[i]=i+1;
}
for (int i = 0; i < m; ++i) {
for (int j = 0; j < m && B[i] > 1; ++j) {
int g = gcd(A[j], B[i]);
A[j] /= g;
B[i] /= g;
}
}
for(int i=0;i<m;i++){
ans*=A[i];
ans%=mod;
}
return ans;
}
int b[N];
int a[N],s[N],S[N];
void solve() {
init();
int n,x,y;
cin>>n>>x>>y;
x--;
for(int i=0;i<n;i++){
cin>>a[i];
a[i]++;
}
int ans=0;
for(int i=0;i<(1ll<<n);i++){
int sum=0;
int len=0;
for(int j=0;j<n;j++){
if(i>>j&1){
len++;
sum+=a[j];
}
}
if(sum<=y){
if(len%2){
ans+=mod-C(y-sum+n,n);
}else{
ans+=C(y-sum+n,n);
}
ans%=mod;
}
if(sum<=x){
if(len%2){
ans+=C(x-sum+n,n);
}else{
ans+=mod-C(x-sum+n,n);
}
ans%=mod;
}
}
cout<<ans<<endl;
}
D. Blocks
题意
给定长度为 \(n\) 的序列,有红、黄、蓝、绿四种颜色砖块,每块长度 \(1\)。要求用砖块铺满序列,且红色和绿色砖块的数量均为偶数。求方案数模 \(10007\)。\(n \le 10^9\)。
思路
我做题时没想到生成函数的做法,直接用组合数求出来的。
不难推理出方案数为:\(\displaystyle\sum_{i=0}^{\lfloor \frac{n}{2}\rfloor}\binom{n}{2i}2^{n-2i}\displaystyle\sum_{j=0}^i\binom{2i}{2j}\)
(要注意i=0时的值另算)
\(=2^n+\displaystyle\sum_{i=1}^{\lfloor \frac{n}{2}\rfloor}\binom{n}{2i}2^{(n-2i)+(2i-1)}\)
\(=2^n+2^{n-1}\displaystyle\sum_{i=1}^{\lfloor \frac{n}{2}\rfloor}\binom{n}{2i}\)
\(=2^n+2^{n-1}(2^{n-1}-1)\)
\(=4^{n-1} + 2^{n-1}\)
然后是生成函数的做法:
设 \(a_n\) 为满足条件的方案数。考虑指数生成函数:红、绿要求偶数,其 EGF 为 \(\frac{e^x+e^{-x}}{2}\),黄、蓝无限制,EGF 为 \(e^x\)。总的 EGF 为
提取 \(\frac{x^n}{n!}\) 系数,当 \(n \ge 1\) 时得到
代码
点击查看代码
void solve() {
// init();
int n,x,y;
cin>>n;
int ans=qpow(2,n)+qpow(2,n-1)*(qpow(2,n-1)-1+mod)%mod;
ans%=mod;
cout<<ans<<endl;
}
E. Lust
题意:
有 \(n\) 个数 \(a_1,\dots,a_n\),进行 \(k\) 次操作,每次等概率随机选一个下标 \(x\in[1,n]\),将 \(a_x\) 减 \(1\),同时将答案加上除 \(a_x\) 外所有数的乘积。求最终答案的期望,对 \(10^9+7\) 取模。
思路:
设当前所有数乘积为 \(P\)。一次操作选中 \(x\),增加量为 \(P/a_x\),操作后新的乘积 \(P'=(a_x-1)\cdot \frac{P}{a_x}=P-\frac{P}{a_x}\)。因此每次增加量等于乘积的减少量。\(k\) 次操作的总增加量即为初始乘积与最终乘积的差:\(\prod a_i-\prod(a_i-c_i)\),其中 \(c_i\) 是第 \(i\) 个数被选次数。
然后不会了,期望算不来,下次学了概率论再说。
代码
点击查看代码

浙公网安备 33010602011771号