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

版权声明:转载需要注明哦QwQ,地址:http://blog.csdn.net/effervescence。 https://blog.csdn.net/Effervescence/article/details/82225627

分析

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

Code

#include<bits/stdc++.h>
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(n<k)
        return 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<<C(5,2)<<endl;
    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<<"???"<<" "<<x<<" "<<i<<" "<<endl;
            if(x%i==0) {
                for(;q--;puts("0"));
                return 0;
            }
        }
    // cout<<"!!!<<"<<sumd<<endl;
    int m=d.size();
    V=S*m;
    // cout<<V<<" "<<S<<" "<<m<<endl;
    for(int i=0;i<m;++i)
        for(int j=0;j<d[i];++j) {
            // cout<<"???"<<i<<" "<<j<<" "<<d[i]<<endl;
            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<<f[k]<<" "<<g[k]<<endl;
    // cout<<"Done."<<endl;
    while(q--) {
        read(n),n-=sumd,ans=0;// cout<<V<<" "<<n<<" "<<endl;
        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<<f[v+n%S]<<" "<<(n-v)/S+m-1<<" "<<m-1<<endl;
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/Effervescence/article/details/82225627
今日推荐