BZOJ3462 DZY Loves Math II

题目传送门
分析:
首先分析一下性质:
首先,S一定不含某个质数的平方,这里可以特判
p就是所有S的质因数,并且每一个都要出现
那么我们可以先求和sum
n就变成n-sum,那么就可以不要求所有质数一定出现了
再者,质数的种类数会很少,不会超过10
我们设每个质数pi的出现次数为ci
由于pi一定为S的因数,所以pici可以表示为pi(a(S/pi)+b)=aS+bpi
其中ci就分解为a
(S/pi)+b了
前半部分相当于凑齐一个S要用哪一种pi,对于每一种pi,我们使用隔板法选择
后半部分的b由于小于S/pi很小,可以直接考虑完全背包

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define MOD 1000000007

using namespace std;

inline long long getint()
{
	long long num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int S,Q;
int p[10],cnt,num;
long long f[2][15000005],now=1,pre;
long long sum[15000005],inv[10],n;

inline long long C(long long p,long long q)
{
	long long ret=1;p%=MOD;
	for(int i=1;i<=q;i++)ret=ret*(p-i+1)%MOD*inv[i]%MOD;
	return ret;
}

int main()
{
	S=getint(),Q=getint();
	int tmp=S;
	for(int i=2;i*i<=tmp;i++)
		if(tmp%i==0)
		{
			p[++cnt]=i,tmp/=i;
			if(tmp%i==0)
			{
				while(Q--)printf("0
");
				return 0;
			}
		}
	if(tmp>1)p[++cnt]=tmp;
	for(int i=1;i<=cnt;i++)num+=p[i];
	inv[1]=inv[0]=1;
	for(int i=2;i<=cnt;i++)inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;
	f[pre][0]=1;
	for(int i=1;i<=cnt;i++,swap(now,pre))
	{
		for(int j=0;j<p[i];j++)sum[j]=f[now][j]=f[pre][j];
		for(int j=p[i];j<=S*i;j++)
		{
			int k=j%p[i];
			if(j>=S)sum[k]=(sum[k]-f[pre][j-S]+MOD)%MOD;
			sum[k]=(sum[k]+f[pre][j])%MOD;
			f[now][j]=sum[k];
		}
	}
	while(Q--)
	{
		n=getint()-num;
		long long ans=0;
		for(int i=0;i<cnt;i++)
		{
			if(n<i*S)break;
			ans=(ans+f[pre][i*S+n%S]*C(n/S-i+cnt-1,cnt-1))%MOD;
		}
		printf("%lld
",(ans+MOD)%MOD);
	}
}

原文地址:https://www.cnblogs.com/Darknesses/p/12243777.html