【生成函数】CF865G Flowers and Chocolate


终于AC了这道神题。。。

题目

商店里有A种花,每种花一朵有 a i a_i ai片花瓣;有B种巧克力,每种一盒有 b i b_i bi
问N朵花的有序花束和多盒有序的巧克力方案数
且要求巧克力数和花瓣数相同

题解

普通DP
f i f_i fi表示花瓣数 i i i的方案数, g i g_i gi表示巧克力数 i i i的方案数
则普通背包就可以了


发现N极大,就炸了
但如果是生成函数,还是可以尝试一下
F ( x ) = ( ∑ i = 1 A x a i ) N G ( x ) = ∑ j = 0 ∞ ( ∑ i = 1 B x b i ) j = 1 1 − ∑ i = 1 B x b i A n s = ∑ k [ x k ] F ( x ) [ x k ] G ( x ) \begin{aligned} &F(x)=(\sum_{i=1}^Ax^{a_i})^N\\ &G(x)=\sum_{j=0}^{\infin}(\sum_{i=1}^Bx^{b_i})^j=\frac 1{1-\sum_{i=1}^Bx^{b_i}}\\ &Ans=\sum_k[x^k]F(x)[x^k]G(x) \end{aligned} F(x)=(i=1Axai)NG(x)=j=0(i=1Bxbi)j=1i=1Bxbi1Ans=k[xk]F(x)[xk]G(x)
当然也可以
A n s = [ x 0 ] F ( x ) G ( 1 x ) = [ x 0 ] ( ∑ i = 1 A x a i ) N 1 − ∑ i = 1 B x − b i Ans=[x^0]F(x)G(\frac1x)=[x^0]\frac{(\sum_{i=1}^Ax^{a_i})^N}{1-\sum_{i=1}^Bx^{-b_i}} Ans=[x0]F(x)G(x1)=[x0]1i=1Bxbi(i=1Axai)N
聪明的你已经发现了关于极大的N现在已经解决一半了
在快速幂下这么大的N不算什么
但次数太大存不下。。。

于是我们用到了常系数齐次递推
F ( x ) F(x) F(x) M = m a x ( b i ) , H ( x ) = x M − ∑ i = 1 B x M − b i M=max(b_i),H(x)=x^M-\sum_{i=1}^Bx^{M-b_i} M=max(bi),H(x)=xMi=1BxMbi取模,答案不变
说到这里其实已经做完了, F ( x ) F(x) F(x)快速幂取模, G ( x ) G(x) G(x)用普通dp求系数,最后统计答案
甚至不用NTT乘法,直接暴力双循环都可以做

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
inline int in()
{
	int ans=0,f=1;char s=getchar();
	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
	while(isdigit(s))
	{
		ans=ans*10+s-'0';
		s=getchar();
	}return ans*f;
}
const int N=1e3+10,mod=1e9+7;
int Add(int a,int b){return (a+b)%mod;}
int Dec(int a,int b){return a>=b?a-b:a-b+mod;}
int Mul(int a,int b){return 1ll*a*b%mod;}
int tmp[N];
int M,H[N];
void mul(int *A,int *B,int *C)
{
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<M;i++)
		for(int j=0;j<M;j++)
			tmp[i+j]=Add(tmp[i+j],Mul(A[i],B[j]));
	for(int i=2*M-2;i>=M;i--)
		for(int j=M-1;j>=0;j--)
			tmp[i-M+j]=Dec(tmp[i-M+j],Mul(tmp[i],H[j]));
	for(int i=0;i<M;i++)C[i]=tmp[i];
}
void add(int *A,int *B,int *C)
{
	static int tmp[N];memset(tmp,0,sizeof(tmp));
	for(int i=0;i<M;i++)tmp[i]=Add(A[i],B[i]);
	for(int i=0;i<M;i++)C[i]=tmp[i];
}
void quick_mul(int *A,int *B,long long k)
{
	int G[N],F[N];
	memset(G,0,sizeof(G));
	memset(F,0,sizeof(F));
	for(int i=0;i<M;i++)F[i]=A[i];
	G[0]=1;
	while(k)
	{
		if(k&1)mul(G,F,G);
		mul(F,F,F);
		k>>=1;
	}
	for(int i=0;i<M;i++)B[i]=G[i];
}
int f,g,ans;
long long n;
int a[N],b[N],c[N],ls[N],t[N];
int G[N],F[N];
signed main()
{
	f=in();g=in();n=in();
	for(int i=0;i<f;i++)a[i]=in();
	for(int i=0;i<g;i++)c[i]=in(),M=max(M,c[i]),t[c[i]]++;
	for(int i=0;i<g;i++)H[M-c[i]]=Dec(H[M-c[i]],1);
	H[M]=G[0]=1;
	if(M==1)b[0]=Dec(0,H[0]);else b[1]=1;//这里需要特判M,想下为什么
	for(int i=0;i<f;i++)
	{
		memset(ls,0,sizeof(ls));
		quick_mul(b,ls,a[i]);
		add(ls,F,F);
	}
	quick_mul(F,F,n);
	for(int i=0;i<M;i++)
	for(int j=0;j<=i;j++)
	G[i]=Add(G[i],Mul(G[i-j],t[j]));
	for(int i=0;i<M;i++)ans=Add(ans,Mul(F[i],G[i]));
	printf("%lld",(ans+mod)%mod);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值