bzoj 3462: DZY Loves Math II

3462: DZY Loves Math II

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 211  Solved: 103
[Submit][Status][Discuss]

Description

Input

 

第一行,两个正整数 S 和 q,q 表示询问数量。
接下来 q 行,每行一个正整数 n。

Output

输出共 q 行,分别为每个询问的答案。

Sample Input

30 3
9
29
1000000000000000000

Sample Output

0
9
450000036

HINT

 

感谢the Loser协助更正数据


对于100%的数据,2<=S<=2*10^6,1<=n<=10^18,1<=q<=10^5

 
 
神dp+组合数学题、
首先如果S有一个质因子的次数>1的话就全输出0;
如果n<∑zs[i]的话也是输出0。
 
首先把n减去所有质因子的和,吧问题转化成每个质因子可以选任意个。
 
然后我们可以发现每个质因子pi能表示的数可以写成 x*S+y*pi ,其中y*pi<S。
这样的话我们就枚举一下所有质因子的x的和,这个的范围区间大小不会超过7,因为∑(y*pi) <S*num
 
然后问题就分成了两个部分:
1.∑xi  =  k,其中xi可以是任意自然数。
2.∑yi*pi  =  n-k*S,其中0<=yi*pi<S。
 
这样的话,第一问题就是一个可重组合问题,直接上组合数;
第二个问题就是多重背包问题,也是直接上多重背包。
最后把两个答案乘起来(两个互不干涉,乘法原理)
 
 
/**************************************************************
    Problem: 3462
    User: JYYHH
    Language: C++
    Result: Accepted
    Time:5736 ms
    Memory:141916 kb
****************************************************************/
 
#include<bits/stdc++.h>
#define ll long long
#define maxn 2000005
using namespace std;
const int ha=1000000007;
ll n;
int d[15],num=0,S,q,now;
int inv[20],f[2][maxn*9];
int zssum=0,ans;
 
inline int add(int x,int y){
    x+=y;
    if(x>=ha) return x-ha;
    else return x;
}
 
inline void dp(){
    f[0][0]=1;
    now=0;
    int pre=now,tp=S*num-zssum;
     
    for(int i=1;i<=num;i++){
        now^=1;
         
//      memset(f[now],0,sizeof(f[now]));
         
        for(int j=0;j<d[i];j++){
            int tot=0;
            for(int u=j;u<=tp;u+=d[i]){
                tot=add(tot,f[pre][u]);
                if(u-j>=S) tot=add(tot,ha-f[pre][u-S]);
                 
                f[now][u]=tot;
            }
        }
         
        pre=now;
    }
}
 
inline int C(ll x,int y){
    int an=1;
    for(int i=1;i<=y;i++) an=an*((ll)(x-i+1)%ha)%ha*(ll)inv[i]%ha;
    return an;
}
 
int main(){
    inv[1]=1;
    for(int i=2;i<=8;i++) inv[i]=-inv[ha%i]*(ll)(ha/i)%ha+ha;
     
    scanf("%d%d",&S,&q);
     
    int U=S;
    for(int i=2;i*(ll)i<=U;i++) if(!(U%i)){
        d[++num]=i,U/=i,zssum+=i;
        if(!(U%i)){
            while(q--) puts("0");
            return 0;
        }
    }
    if(U!=1) d[++num]=U,zssum+=U;
     
    dp();
     
    while(q--){
        ans=0;
         
        scanf("%lld",&n);
         
        if(n<zssum){
            puts("0");
            continue;
        }
         
        n-=zssum;
         
        int tp=min((ll)num,n/S);
        ll tt=n/S,lef=n-tt*S;
        for(int i=0;i<=tp;i++) ans=add(ans,C(tt-i+num-1,num-1)*(ll)f[now][i*S+lef]%ha);
         
        printf("%d\n",ans);
    }
     
    return 0;
}

  

posted @ 2018-02-22 21:41  蒟蒻JHY  阅读(582)  评论(0编辑  收藏  举报