正题
题目链接:https://www.luogu.com.cn/problem/P4495
题目大意
nnn个物品大小为viv_ivi,每个物品有无数个,背包的重量定义为大小和%P\%P%P。
qqq次询问,问一个www表示有多少种取法使得背包重量为www(两种方案不同仅当有一种物品在其他方案种的是否选择不同,与选择数量无关)。
解题思路
对于一个物品viv_ivi在%P\%P%P意义下可以视为一个gcd(vi,P)gcd(v_i,P)gcd(vi,P)的物品。
然后同理对于两个物品a,ba,ba,b可以视为一个gcd(a,b,P)gcd(a,b,P)gcd(a,b,P)的物品,多个物品同理。
先筛出PPP的所有约数,设fif_ifi表示现在的所有gcdgcdgcd为PPP的第iii个约数。然后如果kkk表示PPP的约数个数就可以O(k2logk)O(k^2\log k)O(k2logk)进行dpdpdp。
然后对于一个询问www,如果能够拼出gcd(w,P)gcd(w,P)gcd(w,P)就可以拼出www,所有我们定义gig_igi表示第iii个约数能够有多少种方法被拼出就好了。
时间复杂度O(k2logk+qlogk)O(k^2\log k+q\log k)O(k2logk+qlogk)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=4e4+10,XJQ=1e9+7;
ll n,q,P,cnt,d[N],v[N],g[N],f[N];
int main()
{scanf("%lld%lld%lld",&n,&q,&P);for(ll i=1;i*i<=P;i++){if(P%i==0)d[++cnt]=i;if(P%i==0&&i*i!=P)d[++cnt]=P/i;}sort(d+1,d+1+cnt);for(ll i=1;i<=cnt;i++)v[i]=1;for(ll i=1;i<=n;i++){ll x;scanf("%lld",&x);x=lower_bound(d+1,d+1+cnt,__gcd(x,P))-d;v[x]=v[x]*2%XJQ;}f[cnt]=1;for(ll i=1;i<=cnt;i++){if(!v[i])continue;v[i]=(v[i]-1+XJQ)%XJQ;for(ll j=1;j<=cnt;j++){ll k=__gcd(d[j],d[i]);k=lower_bound(d+1,d+1+cnt,k)-d;(f[k]+=f[j]*v[i]%XJQ)%=XJQ;}}for(ll i=1;i<=cnt;i++)for(ll j=1;j<=i;j++)(g[i]+=f[j]*(d[i]%d[j]==0))%=XJQ;while(q--){scanf("%lld",&n);n=__gcd(n,P);n=lower_bound(d+1,d+1+cnt,n)-d;printf("%lld\n",g[n]);}
}