题意简述
有一个奇怪的背包,它有一个参数 (P) ,当背包内放入若干个物品后,背包的重量是物品总体积对 (P) 取模后的结果。
现有 (n) 种体积不同的物品,第 (i) 种占用体积为 (V_i) ,每种物品都有无限个。
(q) 次询问,每次询问给出重量 (w_i) ,求有多少种放入物品的方案,能将一个初始为空的背包的重量变为 (w_i) 。注意,两种方案被认为是不同的,当且仅当放入物品的种类不同,而与每种物品放入的个数无关。不难发现总的方案数为 (2^n) 。
结果对 998244353 取模。
(n,qleq 10^6) (Pleq 10^9)
想法
很显然能推出要取与 (P) 的 (gcd) 的性质。
预处理出 (P) 的所有因数(个数是 (10^3)) 级别,然后随便 (dp) 或容斥就好了。
有点容易想错,写之前一定要想清楚。
(我好懒)
总结
性质
一个大数 (P) 的因子个数是 (P^{frac{1}{3}}) 级别的。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<map>
#define mod 1000000007
using namespace std;
int read(){
int x=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x;
}
const int N = 1000005;
map<int,int> mp;
int n,Q,P,m,v[N],p[N],t[N],f[N],g[N],ans[N];
int gcd(int a,int b) { return b?gcd(b,a%b):a; }
int Pow_mod(int x,int y) {
int ret=1;
while(y){
if(y&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ret;
}
int Plus(int x,int y) { return x+y>=mod?x+y-mod:x+y; }
int Minus(int x,int y) { return x>=y?x-y:x-y+mod; }
int main()
{
n=read(); Q=read(); P=read();
for(int i=1;i<=n;i++) v[i]=gcd(P,read());
for(int i=1;i*i<=P;i++) {
if(P%i) continue;
p[++m]=i;
}
for(int i=m;i>0;i--){
if(p[i]*p[i]==P) continue;
p[++m]=P/p[i];
}
for(int i=1;i<=m;i++) mp[p[i]]=i;
for(int i=1;i<=n;i++) t[mp[v[i]]]++;
for(int i=1;i<=m;i++){
g[i]=t[i];
for(int j=i+1;j<=m;j++)
if(p[j]%p[i]==0) g[i]+=t[j];
g[i]=Minus(Pow_mod(2,g[i]),1);
}
for(int i=m;i>0;i--){
f[i]=g[i];
for(int j=i+1;j<=m;j++)
if(p[j]%p[i]==0) f[i]=Minus(f[i],f[j]);
}
for(int i=1;i<=m;i++){
ans[i]=Plus(ans[i],f[i]);
for(int j=i+1;j<=m;j++)
if(p[j]%p[i]==0) ans[j]=Plus(ans[j],f[i]);
}
int w;
while(Q--){
w=gcd(read(),P);
printf("%d
",ans[mp[w]]);
}
return 0;
}