BZOJ3462 DZY Loves Math II

题目

首先如果\(S\)中含平方因子或者\(\sum p>x\),直接输出\(0\)

之后我们令\(x\)减去\(\sum p\),之后就变成了一个完全背包问题

背包的体积那么大可咋搞啊

我们注意到对于一个质数\(p_i\),其能表示的数是\(aS+bp_i(bp_i<S)\)的形式,两种方案不同当且仅当存在一个质数\(a\)不同或\(b\)不同,于是我们可以对于两部分分别计算乘法原理合并就好了

由于质因子个数不会超过\(7\),所以所有质数\(bp_i\)的总和不会超过\(7\times S\),我们对这部分跑一个多重背包;对于一个询问\(x\),我们枚举一下这一部分取得总和是多少,剩下的肯定是\(S\)的整数倍,用组合数分配一下就好了

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
const int mod=1e9+7;
int m,S,Q,Sum,Sz,ans;LL n;
int pre[14000005],dp[14000005];
int f[200005],p[100005],Pr[105],inv[105];
inline int dqm(int x) {return x<0?x+mod:x;}
inline int qm(int x) {return x>=mod?x-mod:x;}
inline int C(LL n,int m) {
	if(m>n) return 0;
	int nw=inv[m];
	for(re LL i=n;m;--i,--m) nw=1ll*nw*(i%mod)%mod;
	return nw;
} 
int main() {
	scanf("%d%d",&S,&Q);int R=sqrt(S)+1;m=S;
	for(re int i=2;i<=R;i++) {
		if(!f[i]) p[++p[0]]=i;
		for(re int j=1;j<=p[0]&&p[j]*i<=R;++j) {
			f[p[j]*i]=1;if(i%p[j]==0) break;
		}
	}
	for(re int i=1;i<=p[0];i++) {
		if(S==1) break;if(S%p[i]) continue;
		S/=p[i];if(S%p[i]==0) {while(Q--)puts("0");return 0;}
		Pr[++Pr[0]]=p[i];Sum+=p[i];
	}
	if(S!=1) Pr[++Pr[0]]=S,Sum+=S;dp[0]=1;
	for(re int i=1;i<=Pr[0];++i) {
		Sz+=m-Pr[i];
		for(re int j=0;j<=Sz;++j) {
			if(j>=Pr[i]) pre[j]=qm(pre[j-Pr[i]]+dp[j]);else pre[j]=dp[j];
			dp[j]=dqm(pre[j]-(j<m?0:pre[j-m]));
		}
	}
	inv[0]=inv[1]=1;for(re int i=2;i<=Pr[0];++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(re int i=2;i<=Pr[0];++i) inv[i]=1ll*inv[i]*inv[i-1]%mod;
	for(;Q;--Q,ans=0) {
		scanf("%lld",&n);if(n<Sum) {puts("0");continue;}n-=Sum;
		for(re int i=n%m,tot=0;i<=n&&i<=Sz;i+=m,++tot)
			ans=qm(ans+1ll*dp[i]*C(n/m-tot-1+Pr[0],Pr[0]-1)%mod);
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2019-12-13 07:50  asuldb  阅读(288)  评论(1编辑  收藏  举报