[HAOI2018]奇怪的背包
Description
\(n\) 种权值各不相同的物品,每种无穷个,给定数 \(P\)。\(q\) 次询问,每次问有多少种选物品的方案(每种任意个),使得在模 \(P\) 意义下同余于 \(w\)。方案不同当且仅当物品种类不同。
Solution
容易知道(考虑裴蜀定理),对于一种价值为 \(v\) 的物品,使用无穷次后,得到的值只会是
\[0,\gcd(v,P),2\gcd(v,P),3\gcd(v,P),\dots
\]
所以考虑直接将 \(v\) 变为 \(\gcd(P,v)\)。进一步(多个数的裴蜀定理),对于多种物品,能凑出来的值有
\[0,\gcd(v_1,v_2,\dots),2\gcd(v_1,v_2,\dots),3\gcd(v_1,v_2,\dots),\dots
\]
所以方案合法当且仅当 \(\gcd(v_1,v_2,\dots)|w\)
容易发现 \(\gcd\) 只会取 \(P\) 的约数,所以考虑算每种约数有多少种方案可以凑出。用 \(dp[i][j]\) 表示考虑了前 \(i\) 个约数,\(\gcd\) 是第 \(j\) 个约数的方案。转移就很显然。
算答案只需要枚举 \(w\) 的约数即可。但这样还是不够优秀。容易发现最终的答案其实是 \(w\) 和 \(P\) 的高维前缀的交集。这个交集部分其实就是 \(\gcd(w,P)\),而这一定是 \(P\) 的约数。所以可以考虑预处理高维前缀和。
int main(){
int n=read(),Q=read(),P=read(); pw[0]=1;
for(int i=1;i*i<=P;i++)
if(P%i==0){ p[++cnt]=i; if(i*i!=P) p[++cnt]=P/i;}
sort(p+1,p+1+cnt); for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2ll%Mod;
for(int i=1;i<=n;i++)
a[i]=gcd(read(),P),c[Get(a[i])]++;
for(int i=1;i<=cnt;i++){
for(int k=1;k<=cnt;k++){
int &x=dp[Get(gcd(p[i],p[k]))];
x=(x+1ll*dp[k]*(pw[c[i]]-1)%Mod)%Mod;
}
dp[i]=(dp[i]+pw[c[i]]-1)%Mod;
}
for(int i=cnt;i;i--)
for(int j=1;j<i;j++)
if(p[i]%p[j]==0) dp[i]=(dp[i]+dp[j])%Mod;
while(Q--) printf("%d\n",dp[Get(gcd(P,read()))]);
}