[ 数论 ] [ DP ] BZOJ3462

18 篇文章 0 订阅

S=ki=1pi,n=xipi S = ∏ i = 1 k p i , n = ∑ x i p i
可以发现 k k 最多只有 7
先将 n n 减去 pi ,保证 lcm=S l c m = S
xi x i 表示为 aSpi+b ( b<Spi ) a · S p i + b   (   b < S p i   ) ,这样每一组 (a,b) ( a , b ) 都是一种方案。
n n 也拆分成 nS n mod S n   m o d   S 两部分。显然 n mod S n   m o d   S 只能用 b b 填。枚举 nS 中有几个由 b b <script type="math/tex" id="MathJax-Element-82">b</script> 填,多重背包预处理下,剩下的就可以直接算了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2000010;
const int M=1000000007;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
inline void Read(ll& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
ll n;
int k,m,T,s;
int cnt;
int Ans;
int p[N],num,sum;
int inv[10];
int a[10];
int f[N<<3],g[N<<3],mx;
bool b[N];
inline void Add(int& x,int y){
    x=(x+y)%M;
}
inline bool Init(){
    for(int i=2;i<=s;i++){
        if(!b[i]){
            p[++num]=i;
            if(!(s%i)){
                sum+=i;a[++cnt]=i;
                if(1ll*i*i<=s&&!(s%(i*i)))return 0;
            }
        }
        int t;
        for(int j=1;j<=num&&(t=p[j]*i)<=s;j++){
            b[t]=1;
            if(!(i%p[j]))break;
        }
    }
    inv[0]=inv[1]=1;
    for(int i=2;i<=9;i++)inv[i]=1ll*inv[M%i]*(M-M/i)%M;
    for(int i=3;i<=9;i++)inv[i]=1ll*inv[i-1]*inv[i]%M;
    return 1;
}
inline void DP(){
    mx=cnt*s;
    f[0]=1;
    for(int i=1;i<=cnt;i++){
        for(int j=0;j<a[i];j++){
            int t=0;
            for(int k=j;k<=mx;k+=a[i]){
                Add(t,f[k]);
                if(k>=s)Add(t,-f[k-s]);
                g[k]=t;
            }
        }
        memcpy(f,g,sizeof(g));
    }
}
inline int C(ll n,int m){
    int Ans=1;
    for(ll i=n-m+1;i<=n;i++)Ans=i%M*Ans%M;
    Ans=1ll*Ans*inv[m]%M;
    return Ans;
}
int main(){
    Read(s);Read(T);
    if(!Init()){
        while(T--)printf("0\n");
        return 0;
    }
    DP();
    while(T--){
        Read(n);
        n-=sum;
        if(n<0){
            printf("0\n");
            continue;
        }
        Ans=0;
        ll x=n/s;int y=n%s;
        for(int i=0;i*s+y<=mx&&i<=x;i++)Add(Ans,1ll*f[i*s+y]*C(x-i+cnt-1,cnt-1)%M);
        printf("%d\n",(Ans+M)%M);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值