题解
首先pi都是质数,一群质数的lcm不可能是一个有平方因子的数
所以先把S分解一下质因数,如果有平方因子就全部都输出0
剩下的就是完全背包来统计这几个质因子的和等于n的方案数
其实前几步转换并不难想
考试的时候就想到了这里,然后就联想到了生成函数(下面是脑抽环节)
发现最后算的答案的生成函数就是
然后可以把分母除过去,然后把前S项算出来,剩下的就可以矩阵快速幂(联想到之前的blog),复杂度O(S^3*logn)
但是S是2*10^6,所以就直接爆炸了。。。
可以看出,我们之前的做法根本就没用上条件:
于是就有了一个全新的想法:把一个质数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);
}
}