[ 数论 ] [ DP ] BZOJ3462

本文介绍了一种解决特定数学问题的方法,该问题涉及利用多重背包算法寻找满足特定条件的整数解组合。通过预处理和枚举策略,文章提供了一个有效的解决方案,并附带了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

S=ki=1pi,n=xipiS=∏i=1kpi,n=∑xipi
可以发现 kk 最多只有 7
先将 nn 减去 pi ,保证 lcm=Slcm=S
xixi 表示为 aSpi+b ( b<Spi )a·Spi+b ( b<Spi ) ,这样每一组 (a,b)(a,b) 都是一种方案。
nn 也拆分成 nSn mod Sn mod S 两部分。显然 n mod Sn mod S 只能用 bb 填。枚举 nS 中有几个由 bb 填,多重背包预处理下,剩下的就可以直接算了。

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值