[BZOJ3462]DZY loves Math II(组合数+背包)

分析

  看上去题目的条件非常苛刻,但我们仔细分析题面可以发现, S S 一定不含有平方因子:因为LCM里的数都是质数,指数的最大值都是1,而LCM的本质就是取每个因子的指数最大值,所以S就是给出了一个质数集合。
  所以题目实际是给出了一个指数集合,而背包体积为V,让你求用这个质数集合装满背包的方案数,也就是把题目转化为了一个背包问题。
  而我们发现, n n 非常大,无法进行任何形式的DP,但相比之下S集合就非常小,最小的8个质数相乘也就超过S了,所以S最多只含有7个质数。
  考虑这样一种表示方案的方法:将一种方案表示为一个长度为 k k 的向量 <a1,a2,...,ak> < a 1 , a 2 , . . . , a k > ,表示第 i i 个物品被选择了 ai a i 。这个 ai a i 可以表示成 x(SPi)+y x ∗ ( S P i ) + y ,也就是分为对 SPi S P i 的倍数和余数来处理, x x 不同或者 y y 不同意味着方案就是不同的。如果 x x 不同,那么 x x 每增大 1 1 ,就会占用 S S 的体积。如果要分配每个方案的 x x ,等价于将 nS n S 个物品分到 k k 个盒子内,根据隔板法我们可以得到 Ck1nS+k1 C n S + k − 1 k − 1
  而 y y 的部分我们可以进行背包,因为每个物品的 y y 都不超过 SPi S P i ,所以总容量不会超过 y y 的和(虽然也有 S7 S ∗ 7 那么大,但是背包是可以跑过的)。
  对于 y y 的贡献我们跑背包的时候可以进行分组前缀和优化,而且我们可以发现 x,y x , y 的贡献是相互独立的,说明我们可以将方案相乘。
  可是这里面却包含了 S S 的很多倍,但不能确定究竟是几倍,所以我们需要进行枚举,就是枚举 y y 部分总共占用的容量为 iS+(n%S) i ∗ S + ( n % S ) 来做。
  但我们在求组合数的时候又会出现问题, n n 非常大,不能直接阶乘预处理,而 mod m o d 又有 1e9+7 1 e 9 + 7 ,所以也不能用卢卡斯定理做,但我们发现 k k 非常小,所以我们可以预处理 k! k ! 的逆元,然后暴力 O(k) O ( k ) 计算就行了。

Code

#include
using namespace std;
typedef long long ll;
template<class T>inline void read(T &x,T f=1,char ch=' ') {
    x=0;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-48,ch=getchar();
    x*=f;
}
const int mod=1e9+7;
namespace {
    inline int Add(const int &x,const int &y) {
        int res=x+y;
        return res>=mod?res-mod:res;
    }
    inline int Sub(const int &x,const int &y) {
        int res=x-y;
        return res<0?res+mod:res;
    }
    inline int Mul(const int &x,const int &y) {
        return 1ll*x*y%mod;
    }
    inline int Pow(int x,int y=mod-2,int res=1) {
        for(;y;x=1ll*x*x%mod,y>>=1)
            if(y&1)
                res=1ll*res*x%mod;
        return res;
    }
}
int inv[15],q;
inline int C(const ll &n,const int &k) {
    if(nreturn 0;
    int res=1;
    for(ll i=n;i>n-k;--i)
        res=Mul(res,i%mod);
    return Mul(res,inv[k]);
}
ll S,sumd,V,n,ans;
vector<int>d;
int f[14000001],g[14000001];
int main() {
    inv[0]=1;
    for(int i=1;i<=10;++i)
        inv[i]=Mul(inv[i-1],i);
    inv[10]=Pow(inv[10]);
    for(int i=9;~i;--i)
        inv[i]=Mul(inv[i+1],i+1);
    // cerr<
    read(S),read(q);ll x=S;f[0]=g[0]=1;
    for(int i=2;i<=x;++i)
        if(x%i==0) {
            d.emplace_back(i);
            sumd+=i;
            x/=i;
            // cout<<"???"<<" "<
            if(x%i==0) {
                for(;q--;puts("0"));
                return 0;
            }
        }
    // cout<<"!!!<<"<
    int m=d.size();
    V=S*m;
    // cout<
    for(int i=0;ifor(int j=0;j// cout<<"???"<
            for(int k=d[i]+j;k<=V;k+=d[i])
                g[k]=Add(g[k-d[i]],f[k]);
            for(int k=d[i]+j;k<=V;k+=d[i])
                f[k]=Sub(g[k],k>=S?g[k-S]:0);
        }
    // for(int k=0;k<=V;++k)
    //  cout<
    // cout<<"Done."<
    while(q--) {
        read(n),n-=sumd,ans=0;// cout<
        for(int v=0;v<=min(V,n);v+=S)
            ans=Add(ans,Mul(f[v+n%S],C((n-v)/S+m-1,m-1)));// ,cout<
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(数论,DP)