BZOJ3462 DZY Loves Math II(动态规划+组合数学)
容易发现这是一个有各种玄妙性质的完全背包计数。
对于每个质数,将其选取个数写成ax+b的形式,其中x=S/pi,0<b<x。那么可以枚举b的部分提供了多少贡献,多重背包计算,a的部分直接组合数即可。多重背包计数可以前缀和优化。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define N 2000010 #define ll long long #define P 1000000007 ll read() { ll x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int S,m,prime[N],f[N<<3],g[N<<3],inv[10],cnt=0; int C(ll n,int m) { int s=1; for (ll i=n;i>n-m;i--) s=i%P*s%P; return 1ll*s*inv[m]%P; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj3462.in","r",stdin); freopen("bzoj3462.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif S=read(),m=read(); inv[1]=1;for (int i=2;i<=9;i++) inv[i]=P-1ll*(P/i)*inv[P%i]%P; for (int i=2;i<=9;i++) inv[i]=1ll*inv[i]*inv[i-1]%P; bool flag=1; for (int i=2;i<=S;i++) if (S%i==0) { prime[++cnt]=i;S/=i; if (S%i==0) {flag=0;break;} if (S==1) break; } for (int i=1;i<=cnt;i++) S*=prime[i]; f[0]=1; for (int i=1;i<=cnt;i++) { g[0]=1; for (int k=prime[i];k<=S*7;k++) { g[k]=(g[k-prime[i]]+f[k])%P; f[k]=(g[k]-(k>=S?g[k-S]:0)+P)%P; } } while (m--) { ll n=read(); if (!flag) printf("0\n"); else { for (int i=1;i<=cnt;i++) n-=prime[i]; if (n<0) printf("0\n"); else { int ans=0; for (int i=0;i<=cnt;i++) if (i*S<=n) ans=(ans+1ll*C(n/S-i+cnt-1,cnt-1)*f[n%S+i*S]%P)%P; printf("%d\n",ans); } } } return 0; }