题解

首先pi都是质数,一群质数的lcm不可能是一个有平方因子的数

所以先把S分解一下质因数,如果有平方因子就全部都输出0

剩下的就是完全背包来统计这几个质因子的和等于n的方案数

其实前几步转换并不难想

 

考试的时候就想到了这里,然后就联想到了生成函数(下面是脑抽环节)

发现最后算的答案的生成函数就是

F(x)=\prod_{i=1}^{k}\frac{1}{1-p_i}

然后可以把分母除过去,然后把前S项算出来,剩下的就可以矩阵快速幂(联想到之前的blog),复杂度O(S^3*logn)

但是S是2*10^6,所以就直接爆炸了。。。

 

可以看出,我们之前的做法根本就没用上条件:S=\prod_{i=1}^{k}p_i

于是就有了一个全新的想法:把一个质数p对于和的贡献分为a*S+b*p 

(    b<S/p,否则就可以再分S/p个p,变成(a+b/(S/p))*S+(b%(S/p))*p     )

那么前面的整段贡献就可以用隔板法来计算(把第 i 段分给第 i 个质因子)

后面的k*S段就要用背包来计算方案数

但是这里的背包有限制,就是一个质因子p的选用次数不能超过S/p次,所以多算的还要减掉

为什么可以直接减?

因为方案计数是有可加性的,但是像取min,取max这样的操作就不具有可加性,这时就必须二进制分组(或单调队列)了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 14000005
#define LL long long
const int mod=1000000007;
int f[N],inv[15],a[15],cnt;
int C(LL n,int m)
{
	int ret=1,i;
	for(i=1;i<=m;i++)
		ret=1ll*ret*((n-i+1)%mod)%mod*inv[i]%mod;  
	return ret;
}
int main()
{
	freopen("prime.in","r",stdin);
	freopen("prime.out","w",stdout);
	int S,T,tmp,sum=0,ans,i,j;LL x;
	bool flg=0;
	inv[0]=inv[1]=1;
	for(i=2;i<=9;i++)
		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	scanf("%d%d",&S,&T);tmp=S;
	for(i=2;i*i<=tmp;i++){
		if(tmp%i==0){
			a[++cnt]=i;tmp/=i;sum+=i;
			if(tmp%i==0){flg=1;break;}
		}
	}
	if(tmp>1)a[++cnt]=tmp,sum+=tmp;
	if(!flg){
		f[0]=1;
		for(i=1;i<=cnt;i++){
			for(j=a[i];j<=i*S;j++){
				f[j]+=f[j-a[i]];
				if(f[j]>=mod)f[j]-=mod;
			}
			for(j=i*S;j>=S;j--){
				f[j]-=f[j-S];
				if(f[j]<0)f[j]+=mod;
			}
		}
	}
	while(T--){
		scanf("%lld",&x);x-=sum;
		if(x<0||flg){
			printf("%d\n",0);
			continue;
		}
		ans=0;
		for(i=1;i<=cnt&&(i-1)*S<=x;i++)
			ans=(1ll*ans+1ll*f[(i-1)*S+x%S]*C((x-(i-1)*S)/S-1+cnt,cnt-1))%mod;
		printf("%d\n",ans);
	}
}