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;
}

 

 

 

posted @ 2018-09-22 15:22  Gloid  阅读(223)  评论(0编辑  收藏  举报