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