[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章