HAOI2018 奇怪的背包

传送门

这道题很神奇……
首先我们考虑只有一个物品的情况。通过观察发现,只要\(gcd(v,P) | w\)那么就可以,否则就不行。
然后推广到多个物品的时候,我们发现仍然是成立的,就是对于多个物品取gcd。
我们可以发现每个物品的价值v和\(gcd(v,P)\)是完全等价的,可以直接变成\(gcd(v,P)\),那么我们现在所有物品的价值就都是P的因子了。
于是我们考虑DP。令\(dp[i][j]\)表示选取了P的i个因子,这i个因子的gcd是p的第j个因子的方案数。那么转移方程就是\(dp[i][j] = dp[i-1][j] + \sum_{gcd(a[i],a[j]) = a[k]}dp[i-1][k] * 2^{s[i]}-1\) ,其中s[i]表示第i个因子出现的次数。出现x次的话就有\(2^x-1\)种情况被选中,少的一种是全不选的。
这样的话,令P的因子数为C,对于每次询问,答案就是\(\sum_{a[i] | w}dp[n][i]\),但是这个复杂度是\(O(qC)\)的,有可能会超时。
考虑到\(a[i] | w,a[i] | P\),那么显然\(a[i] | gcd(P,w)\),也就是某一个P的因子。
对于这个嘛,我们在DP之后就可以预处理出\(g[i] = \sum_{a[j] | i}dp[n][j]\),这样就可以\(O(1)\)处理每一次询问,然后就可以\(O(q)\)通过这道题了……

不知道为什么我不用滚动数组就一直在错……用滚动数组就过了……

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define fi first
#define sc second

using namespace std;
typedef long long ll;
const int M = 4005;
const int N = 1000005;
const int mod = 1e9+7;

int read()
{
    int ans = 0,op = 1;char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9') ans *= 10,ans += ch - '0',ch = getchar();
    return ans * op;
}

int n,q,P,v[N],po[N],tot[N],dp[8005][2005],g[8005],cnt,num[M],x,now;
int gcd(int a,int b){return b ? gcd(b,a%b) : a;}
int inc(int a,int b){return (a+b) % mod;}
int mul(int a,int b){return 1ll * a * b % mod;}

void init()
{
   po[1] = 2;
   rep(i,2,n) po[i] = mul(po[i-1],2);
   rep(i,1,n) po[i] = inc(po[i],mod-1);
   rep(i,1,sqrt(P))
   {
      if(P % i == 0)
      {
     num[++cnt] = i;
     if(i * i != P) num[++cnt] = P / i;
      }
   }
   sort(num+1,num+1+cnt);
   rep(i,1,n)
   {
      int cur = lower_bound(num+1,num+1+cnt,v[i]) - num;
      tot[cur]++;
   }
   rep(i,1,cnt)
   {
      if(!tot[i]) continue;
      now ^= 1;
      rep(j,1,cnt) dp[now][j] = dp[now^1][j];
      rep(j,1,cnt)
      {
     if(!dp[now^1][j]) continue;
     int cur = gcd(num[i],num[j]);
     int pos = lower_bound(num+1,num+1+cnt,cur) - num;
     dp[now][pos] = inc(dp[now][pos],mul(dp[now^1][j],po[tot[i]]));
      }
      dp[now][i] = inc(dp[now][i],po[tot[i]]);
   }
   rep(i,1,cnt)
      rep(j,1,i) if(num[i] % num[j] == 0) g[i] = inc(g[i],dp[now][j]);
}

int main()
{
   n = read(),q = read(),P = read();
   rep(i,1,n) v[i] = read(),v[i] = gcd(v[i],P);
   init();
   while(q--)
   {
      x = read(),x = gcd(x,P);
      int cur = lower_bound(num+1,num+1+cnt,x) - num;
      printf("%d\n",g[cur]);
   }
   return 0;
}

posted @ 2019-03-26 21:19  CaptainLi  阅读(143)  评论(0编辑  收藏  举报