您的位置:首页 > 其它

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

2017-10-09 08:51 393 查看

题意



2<=S<=2*10^6,1<=n<=10^18,1<=q<=10^5

分析

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

首先不难发现一些性质:若有解则S一定不含平方因子且k不大于7。

因为n很大但S比较小,我们考虑对S进行处理。

这题的思路是这样的:

因为每一个p都要出现,我们先把n减去每一个p,这样就变成了每个p可以选择不出现。

设n=p1∗c1+p2∗c2+...+pk∗ck

因为pi一定是S的因数,所以pi∗ci一定可以表示成x∗S+y∗pi的形式,其中y∗pi=pi∗cimodS

那么就有ci=x∗Spi+y。

注意到每x+1,就会增加一个S。因为1<=y<Spi,所以有y∗pi<S那么我们可以对多出来的这部分进行多重背包。

对于一个询问n,枚举一个i表示用y组成了i∗S+nmodS(0<=i<k),然后剩下的S就可以分配给这k个素数,方案就是一个组合数,可以用隔板法来求。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
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;j<p[i];j++)
{
int 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<tot&&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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: