[LOJ2523] 「HAOI2018」奇怪的揹包(数论+DP)

题意

  • 有一个奇怪的揹包,揹包的重量是放入其中的物品总体积对PP取模后的结果。现有nn种体积不同的物品,第ii种占用体积为ViV_i,每种物品都有无限个。qq次询问,每次询问给出重量wiw_i,你需要回答有多少种放入物品的方案,能将一个初始为空的揹包的重量变为wiw_i。注意,两种方案被认为是不同的,当且仅当放入物品的种类不同。输出答案对109+710^9 + 7取模的结果。

首先对于一个体积为vv的物品,能取到的方案为kgcd(v,P)(k[1,Pgcd(v,P)]k * gcd(v, P)(k∈[1, \frac{P}{ gcd(v, P)}],那么我们可以把这个物品的体积看作gcd(v,P)gcd(v, P),那么体积相同的物品是可以合并的,又因为1e91e9以内一个数的约数个数是O(n13)O(n^{\frac{1}{3}})级别的,所以物品数就变成了P13P^{\frac{1}{3}}

我们可以写出一个dpdp方程,设f[i][j]f[i][j]为取了前ii个物品,当前选的物品的gcdgcd最小数是PP的第jj个约数的方案数,那么转移可以用刷表法,枚举用哪个取转移,可以转移到gcd(a[i],a[j])gcd(a[i], a[j])这个数上,再把之前的方案累加上去就做完了。

但是我们现在查询的话要枚举一个数的所有约数,是O(P13)O(P^{\frac{1}{3}})级别的,对于这个我们枚举一个数倍数,用O(P13lnP13)O(P^{\frac{1}{3}}\ln P^{\frac{1}{3}})的时间处理所有的答案,那么我们就可以O(logw)O(\log w)的时间回答询问了。

#include<bits/stdc++.h>
#include<bits/extc++.h>

#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define FOR(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define debug(x) cout << #x << " = " << x << endl
#define mem(a, b) memset(a, b, sizeof(a))
#define cpy(a, b) memcpy(a, b, sizeof(a))
#define hsb __gnu_pbds::gp_hash_table
#define min(a, b) (a < b ? a : b)
#define max(a, b) (b < a ? a : b)
#define getc getchar_unlocked
#define inf (0x3f3f3f3f)
#define INF (1e18)
#define pb push_back
#define mp make_pair
#define x first
#define y second
#define y1 orzorz
#define div ylsakaaa

typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;
typedef std::pair<ll, int> PLI;
typedef std::pair<int, int> PII;
typedef long double ldb;
typedef double db;

inline int read() {
	int _ = 0; static char __; int ___ = 1;
	for(__ = getc(); !isdigit(__); __ = getc()) if(__ == '-') ___ = -1;
	for(; isdigit(__); __ = getc()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
	return _ * ___;
}

using namespace std;

const int N = 1e6 + 10;
const int M = 4e3 + 10;
const int mod = 1e9 + 7;

int f[M][M], a[N], div[N], pow2[N], s[M], g[M];
int n, q, P, cnt;

void get_div(int mx) {
	For(i, 1, mx) if(P % i == 0) 
		div[++ cnt] = i, div[++ cnt] = P / i;
	sort(div + 1, div + cnt + 1);
	cnt = unique(div + 1, div + cnt + 1) - div - 1;
}

int main() {
#ifdef ylsakioi
	file("4495");
#endif
	n = read(), q = read(), P = read();
	pow2[0] = 1; get_div(sqrt(P));
	For(i, 1, n) {
		++ s[lower_bound(div + 1, div + cnt + 1, a[i] = __gcd(P, read())) - div];
		pow2[i] = pow2[i - 1] * 2 % mod;
	}
	sort(a + 1, a + n + 1); n = unique(a + 1, a + n + 1) - a - 1;
	For(i, 1, n) {
		int tmp = lower_bound(div + 1, div + cnt + 1, a[i]) - div;
		f[i][tmp] = pow2[s[tmp]] - 1;
		For(j, 1, cnt) {
			int to = lower_bound(div + 1, div + cnt + 1, __gcd(a[i], div[j])) - div;
			(f[i][to] += (ll)f[i - 1][j] * (pow2[s[tmp]] - 1) % mod) %= mod;
		}
		For(j, 1, cnt) (f[i][j] += f[i - 1][j]) %= mod;
	}
	For(j, 1, cnt) For(k, j, cnt) 
		if(div[k] % div[j] == 0)
			(g[k] += f[n][j]) %= mod;
	while(q --) printf("%d\n", g[lower_bound(div + 1, div + cnt + 1, __gcd(P, read())) - div]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章