[BZOJ3462]DZY Loves Math II

匿名 (未验证) 提交于 2019-12-03 00:21:02


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

观察可得S的质因子的次幂不超过1,而且不超过6个,设有k个。
这让人浮想联翩。
设质因子piѡci个计入拆分。
那么n=ipici。现在问题是n很大,我们不能直接做,而且有105组询问。
n很大,我们考虑一种合法方案,每个ci都不小,而p又是s的约数,我们尝试给ci%=spi,我们模了之后一样的方案归为一类,那么对这类的方案我们再把许多的s分配给不同的pi,就可以还原出原来的方案,那么此时假设要分配x个s,那么我们乘上系数Cx+k1k1
我们现在只需要算出恰好要分配x个s的类的方案就可以算出总方案数了,注意到模了之后每个ci不超过s/pi,我们完全可以做一个有个数限制的背包,做出当i=1..kpici=y的每个y的方案数,回答的时候就可以直接弄了,一次询问是O(k)的,不过内存访问似乎不太连续…
注意到一个质因子至少被选一次,那么我们可以先让n减掉每个质因子,这样就不用考虑选没选了。

#include<cstdio>  #include<algorithm> #include<cstring> #include<cmath> #include<map> //开 O2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  using namespace std; #define fo(i,j,k) for(i=j;i<=k;i++) #define fd(i,j,k) for(i=j;i>=k;i--) #define cmax(a,b) (a=(a>b)?a:b) #define cmin(a,b) (a=(a<b)?a:b) typedef long long ll; typedef long long LL; typedef double db; const int N=2000+5,mo=1e9+7,M=2e7+5; int i,S,term,j,d[N],td,rev[N],q,k; int f[M],g[M],h[1000005]; ll ans,rem,di,n; void predo(int S) {     j=S;     fo(i,2,S)     if (S%i==0)     {         d[++td]=i;         S/=i;         if (S%i==0)          {             term=1;             return ;         }     }     if (S>1) d[++td]=S;     S=j;     f[0]=1;     fo(i,1,td)     {         fo(j,0,S*i) g[j]=f[j];         fo(j,0,S*i)         {             k=j%d[i];             h[k]=(h[k]+g[j]-((j-S>=0)?g[j-S]:0))%mo;             if (h[k]<0) h[k]+=mo;             f[j]=h[k];         }     } } int ksm(int x,int y) {     int ret=1;     while (y)     {         if (y&1) ret=1ll*ret*x%mo;         y>>=1;         x=1ll*x*x%mo;     }     return ret; } ll c(ll m,ll n) {     ll i,ret=1;     fd(i,m,m-n+1) ret=ret*(i%mo)%mo;     fo(i,1,n) ret=ret*rev[i]%mo;     return ret; } int main() {     freopen("1.in","r",stdin);     //freopen("1.out","w",stdout);     scanf("%d %d",&S,&q);     predo(S);     fo(i,1,10) rev[i]=ksm(i,mo-2);     fo(i,1,q)     {         ans=0;         scanf("%lld",&n);         fo(j,1,td) n-=d[j];         if (term||n<0)         {             printf("0\n");             continue;         }         fo(j,0,td)         {             rem=n%S+j*S;             di=n/S-j;             if (di<0) break;             ans=(ans+f[rem]*c(di+td-1,td-1))%mo;         }         printf("%lld\n",ans);     } }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!