bzoj 3462: DZY Loves Math II 动态规划+组合数

题意

bzoj 3462: DZY Loves Math II 动态规划+组合数_第1张图片
2<=S<=2*10^6,1<=n<=10^18,1<=q<=10^5

分析

膜了题解,很好的一道题。

首先不难发现一些性质:若有解则S一定不含平方因子且k不大于7。
因为n很大但S比较小,我们考虑对S进行处理。
这题的思路是这样的:
因为每一个p都要出现,我们先把n减去每一个p,这样就变成了每个p可以选择不出现。
n=p1c1+p2c2+...+pkck
因为 pi 一定是S的因数,所以 pici 一定可以表示成 xS+ypi 的形式,其中 ypi=picimodS
那么就有 ci=xSpi+y
注意到每x+1,就会增加一个S。因为 1<=y<Spi ,所以有 ypi<S 那么我们可以对多出来的这部分进行多重背包。
对于一个询问 n ,枚举一个i表示用y组成了 iS+nmodS(0<=i<k) ,然后剩下的S就可以分配给这k个素数,方案就是一个组合数,可以用隔板法来求。

代码

#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int MOD=1000000007;

int s,p[10],tot,f[14000005],g[14000005],ny[10],sum;

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y&1) ans=(LL)ans*x%MOD;
        x=(LL)x*x%MOD;y>>=1;
    }
    return ans;
}

void divi()
{
    int tmp=s;
    for (int i=2;i*i<=tmp;i++)
        if (tmp%i==0)
        {
            if (tmp%(i*i)==0)
            {
                tot=0;return;
            }
            while (tmp%i==0) tmp/=i;
            p[++tot]=i;sum+=i;
        }
    if (tmp>1) p[++tot]=tmp,sum+=tmp;
}

void prework()
{
    divi();
    f[0]=1;
    for (int i=1;i<=tot;i++)
    {
        for (int j=0;j<=tot*s;j++) g[j]=f[j];
        for (int j=0;jint w=0;
            for (int k=j;k<=tot*s;k+=p[i])
            {
                w+=g[k];w-=w>=MOD?MOD:0;
                if (k-s>=0) w-=g[k-s],w+=w<0?MOD:0;
                f[k]=w;
            }
        }
    }
    ny[0]=1;
    for (int i=1;i<=tot;i++) ny[i]=(LL)ny[i-1]*ksm(i,MOD-2)%MOD;
}

int C(LL x,int y)
{
    int ans=ny[y-1];x%=MOD;
    for (int i=0;i<y-1;i++) ans=(LL)(x+y-1-i)*ans%MOD;
    return ans;
}

int main()
{
    scanf("%d",&s);
    prework();
    int T;scanf("%d",&T);
    if (!tot)
    {
        while (T--) puts("0");
        return 0;
    }
    while (T--)
    {
        LL n;
        scanf("%lld",&n);
        n-=sum;
        if (n<0)
        {
            puts("0");
            continue;
        }
        LL ans=0;
        for (int i=0;i*s+n%s<=n;i++) (ans+=(LL)f[i*s+n%s]*C(n/s-i,tot))%=MOD;
        ans+=ans<0?MOD:0;
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(数学,动态规划,组合数学)