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;
}
当你意识到,每个上一秒都成为永恒。