BZOJ 3462 DZY Loves Math II ——动态规划 组合数

好题。

首先发现$p$是互质的数。

然后我们要求$\sum_{i=1}^{k} pi*xi=n$的方案数。

然后由于$p$不相同,可以而$S$比较小,都是$S$的质因数

可以考虑围绕$S$进行动态规划。

然后发现有时候许多情况是多余的。因为一整个$S$只能由一些相同的$p$组合而成。

所以这些部分可以用组合数计算,剩下的部分可以用背包处理出来。

需要滚动数组,而且需要前缀和转移。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define maxn 2000005
#define md 1000000007
int pri[10],f[2][maxn<<3],s,q,top=0;
int Dp()
{
    int now=0,pre=1;
    memset(f[now],0,sizeof f[now]);
    f[now][0]=1;
    F(i,1,top)
    {
        now^=1;pre^=1;memset(f[now],0,sizeof f[now]);
        int up=s/pri[i]-1;
        F(l,0,pri[i]-1)
        {
            int presum=0;
            for (int j=0;j<=(s*top-l)/pri[i];j++)
            {
                presum+=f[pre][j*pri[i]+l];presum%=md;
                if (j>=up+1) presum-=f[pre][(j-up-1)*pri[i]+l];
                f[now][j*pri[i]+l]=presum;
            }
        }
    }
    return now;
}
 
int ksm(int a,int b)
{
    int ret=1;
    for (;b;b>>=1,a=a*a%md) if (b&1) ret=ret*a%md;
    return ret;
}
 
int C(int n,int m)
{
    n=n+1; m=m-1;
    n=n+m-1;
    int ret=1;
    for(int i=n;i>=n-m+1;i--)
        ret=ret*(i%md)%md;
    F(i,1,m) ret=ret*ksm(i,md-2)%md;
    return ret;
}
 
signed main()
{
    scanf("%lld%lld",&s,&q);int x=s;
    F(i,2,sqrt(s))
    {
        if (s%i==0) s/=i,pri[++top]=i;
        if (s%i==0)
        {
            while(q--) printf("0\n");
            return 0;
        }
    }
    if (s>1) pri[++top]=s; s=x;
    int now=Dp();
    while(q--)
    {
        int ret=0;
        int n;scanf("%lld",&n);
        F(i,1,top) n-=pri[i];
        if (n<0) {printf("0\n"); continue;}
        int m=n/s,k=n-m*s;
        F(i,0,min(top,m))
            ret=(ret+f[now][i*s+k]*C(top+m-i-top,top%md)%md)%md;
        printf("%lld\n",(ret+md)%md);
    }
}

  

posted @ 2017-05-05 16:12  SfailSth  阅读(624)  评论(0编辑  收藏  举报