1. 程式人生 > >BZOJ5302 HAOI2018奇怪的揹包(動態規劃)

BZOJ5302 HAOI2018奇怪的揹包(動態規劃)

  由裴蜀定理,子集S有解當且僅當gcd(S,P)|w。

  一個顯然的dp是設f[i][j]為前i個數gcd為j的選取方案。注意到這裡的gcd一定是P的約數,所以狀態數是n√P的。然後可以通過這個得到gcd是j約數的選取方案。複雜度O(n√PlogP)。

  考慮優化。注意到每個數取gcd後的貢獻僅與其和P的gcd有關,而這又一定是P的約數,所以本質不同的物品數量也是O(√P)。那麼上面的dp就可以優化到O(PlogP)了。當然這裡的P是P的約數個數的平方,這顯然是遠遠達不到P的。

#include<iostream> 
#include<cstdio>
#include
<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 1000010 #define K 2010 #define P 1000000007 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return
c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,k,d[K],cnt[K],f[K][K],ans[K],t; inline
void inc(int &x,int y){x+=y;if (x>=P) x-=P;} int main() { #ifndef ONLINE_JUDGE freopen("bzoj5302.in","r",stdin); freopen("bzoj5302.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),k=read(); for (int i=1;i*i<=k;i++) if (k%i==0) { d[++t]=i;cnt[t]=1; if (i*i!=k) d[++t]=k/i,cnt[t]=1; } sort(d+1,d+t+1); for (int i=1;i<=n;i++) (cnt[lower_bound(d+1,d+t+1,gcd(k,read()))-d]<<=1)%=P; f[0][0]=1; for (int i=1;i<=t;i++) for (int j=0;j<=t;j++) inc(f[i][j],f[i-1][j]),inc(f[i][lower_bound(d+1,d+t+1,gcd(d[i],d[j]))-d],1ll*f[i-1][j]*(cnt[i]-1)%P); for (int i=1;i<=t;i++) for (int j=1;j<=i;j++) if (d[i]%d[j]==0) inc(ans[i],f[t][j]); for (int i=1;i<=m;i++) printf("%d\n",ans[lower_bound(d+1,d+t+1,gcd(k,read()))-d]); return 0; }