3462: DZY Loves Math II
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 211 Solved: 103
[Submit][Status][Discuss]
Description
Input
第一行,两个正整数 S 和 q,q 表示询问数量。
接下来 q 行,每行一个正整数 n。
Output
输出共 q 行,分别为每个询问的答案。
Sample Input
30 3
9
29
1000000000000000000
9
29
1000000000000000000
Sample Output
0
9
450000036
9
450000036
HINT
感谢the Loser协助更正数据
对于100%的数据,2<=S<=2*10^6,1<=n<=10^18,1<=q<=10^5
神dp+组合数学题、
首先如果S有一个质因子的次数>1的话就全输出0;
如果n<∑zs[i]的话也是输出0。
首先把n减去所有质因子的和,吧问题转化成每个质因子可以选任意个。
然后我们可以发现每个质因子pi能表示的数可以写成 x*S+y*pi ,其中y*pi<S。
这样的话我们就枚举一下所有质因子的x的和,这个的范围区间大小不会超过7,因为∑(y*pi) <S*num
然后问题就分成了两个部分:
1.∑xi = k,其中xi可以是任意自然数。
2.∑yi*pi = n-k*S,其中0<=yi*pi<S。
这样的话,第一问题就是一个可重组合问题,直接上组合数;
第二个问题就是多重背包问题,也是直接上多重背包。
最后把两个答案乘起来(两个互不干涉,乘法原理)
/************************************************************** Problem: 3462 User: JYYHH Language: C++ Result: Accepted Time:5736 ms Memory:141916 kb ****************************************************************/ #include<bits/stdc++.h> #define ll long long #define maxn 2000005 using namespace std; const int ha=1000000007; ll n; int d[15],num=0,S,q,now; int inv[20],f[2][maxn*9]; int zssum=0,ans; inline int add(int x,int y){ x+=y; if(x>=ha) return x-ha; else return x; } inline void dp(){ f[0][0]=1; now=0; int pre=now,tp=S*num-zssum; for(int i=1;i<=num;i++){ now^=1; // memset(f[now],0,sizeof(f[now])); for(int j=0;j<d[i];j++){ int tot=0; for(int u=j;u<=tp;u+=d[i]){ tot=add(tot,f[pre][u]); if(u-j>=S) tot=add(tot,ha-f[pre][u-S]); f[now][u]=tot; } } pre=now; } } inline int C(ll x,int y){ int an=1; for(int i=1;i<=y;i++) an=an*((ll)(x-i+1)%ha)%ha*(ll)inv[i]%ha; return an; } int main(){ inv[1]=1; for(int i=2;i<=8;i++) inv[i]=-inv[ha%i]*(ll)(ha/i)%ha+ha; scanf("%d%d",&S,&q); int U=S; for(int i=2;i*(ll)i<=U;i++) if(!(U%i)){ d[++num]=i,U/=i,zssum+=i; if(!(U%i)){ while(q--) puts("0"); return 0; } } if(U!=1) d[++num]=U,zssum+=U; dp(); while(q--){ ans=0; scanf("%lld",&n); if(n<zssum){ puts("0"); continue; } n-=zssum; int tp=min((ll)num,n/S); ll tt=n/S,lef=n-tt*S; for(int i=0;i<=tp;i++) ans=add(ans,C(tt-i+num-1,num-1)*(ll)f[now][i*S+lef]%ha); printf("%d ",ans); } return 0; }