题解:P12470 [Math×Girl] 互质与整除
UnratedCheater · · 题解
对于任意正整数
这时候看看题目,由于
-
对于
x 的任意质因子q_i ,必定满足q_i-1 \mid n ,即q_i-1 必是n 的约数。 -
如果某个质因子在
x 中的指数\beta_i > 1 ,则\varphi(x) 中就会产生q_i 这个因子。所以若\varphi(x) \mid n ,那q_i 必须是n 的质因子之一,即q_i \in \{p_1, \dots, p_s\} 。
做质数筛时可以用 米勒-拉宾素性检验,将每一个约数加
将
我们定义背包里的“物品”合法的条件为,遍历
则如果我们要将物品分类,就可以分为以下两种。
-
如果
q 不在\{p_1, \dots, p_s\} 中,根据条件2 ,它的指数\beta 只能为1 ,则这种物品最多只能被选一次,即为 0/1 背包。 -
如果
q = p_k ,它的指数\beta 可以大于1 。选第1 次时,消耗的体积是p_k-1 的质因数分解;从选第2 次开始,每次只需消耗 1 个p_k ,即为完全背包。
到这里基本就结束了。接下来就是递归遍历所有合法的多维体积,然后滚动数组状态转移即可。完全背包也可以用前缀和优化同维转移。
质数筛最劣复杂度是
#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
int qcnt, ctmp[16], palp[16], pc[16], pstr[16];
template<typename T>
inline void read(T &x) {
x = 0;
T f = 1;
char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + (c ^ 48); c = getchar(); }
x *= f;
}
template<typename T>
inline void write(T x, char ec = '\n') {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10, 0);
putchar(x % 10 + '0');
if (ec) putchar(ec);
}
template<typename T>
inline T qpow(T a, T b, T m) {
T ans = 1;
a %= m;
while (b) {
if (b & 1) ans = (ans * a) % m;
a = (a * a) % m;
b >>= 1;
}
return ans;
}
template<typename T>
inline T gcd(T a, T b) {
return b ? gcd(b, a % b) : a;
}
using u64 = unsigned long long;
using u128 = __uint128_t;
inline u64 qp_mr(u64 a, u64 b, u64 m) {
u64 rs = 1;
a %= m;
while (b) {
if (b & 1) rs = (u128)rs * a % m;
a = (u128)a * a % m;
b >>= 1;
}
return rs;
}
bool is_prm(u64 n) {
if (n < 2) return false;
if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11) return true;
if (n == 13 || n == 17 || n == 19 || n == 23 || n == 29) return true;
if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0 || n % 11 == 0) return false;
if (n % 13 == 0 || n % 17 == 0 || n % 19 == 0 || n % 23 == 0 || n % 29 == 0) return false;
u64 d = n - 1;
int s = 0;
while ((d & 1) == 0) { d >>= 1; ++s; }
static const u64 base[] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022};
for (u64 a : base) {
u64 p = a % n;
if (p == 0) continue;
u64 x = qp_mr(p, d, n);
if (x == 1 || x == n - 1) continue;
bool ok = false;
for (int i = 1; i < s; ++i) {
x = (u128)x * x % n;
if (x == n - 1) { ok = true; break; }
}
if (!ok) return false;
}
return true;
}
struct Prm {
u64 q;
int c[16];
int pk;
};
Prm Q[105000];
int poff, ppk, ps, dp[105000];
u64 p[16];
void getQ(int d, u64 cur) {
if (d == ps) {
u64 q = cur + 1;
if (is_prm(q)) {
int pk = -1;
for (int i = 0; i < ps; ++i) {
if (q == p[i]) { pk = i; break; }
}
Q[qcnt].q = q;
for (int i = 0; i < ps; ++i) Q[qcnt].c[i] = ctmp[i];
Q[qcnt].pk = pk;
qcnt++;
}
return;
}
u64 ppow = 1;
for (int i = 0; i <= palp[d]; ++i) {
ctmp[d] = i;
getQ(d + 1, cur * ppow);
if (i < palp[d]) ppow *= p[d];
}
}
void upd(int d, int id, int ek) {
if (d == ps) {
if (ppk == -1) {
int prv = id - poff;
dp[id] += dp[prv];
if (dp[id] >= MOD) dp[id] -= MOD;
} else {
unsigned long long sm = 0;
int mxb = ek - pc[ppk] + 1;
int cid = id - poff;
for (int b = 1; b <= mxb; ++b) {
sm += dp[cid];
cid -= pstr[ppk];
}
dp[id] = (dp[id] + sm) % MOD;
}
return;
}
for (int e = palp[d]; e >= pc[d]; --e) {
upd(d + 1, id + e * pstr[d], d == ppk ? e : ek);
}
}
void solve() {
read(ps);
for (int i = 0; i < ps; ++i) {
read(p[i]);
read(palp[i]);
}
pstr[0] = 1;
for (int i = 0; i < ps; ++i) {
pstr[i + 1] = pstr[i] * (palp[i] + 1);
}
int dpsz = pstr[ps];
for (int i = 0; i < dpsz; ++i) dp[i] = 0;
dp[0] = 1;
qcnt = 0;
getQ(0, 1);
for (int i = 0; i < qcnt; ++i) {
ppk = Q[i].pk;
poff = 0;
for (int j = 0; j < ps; ++j) {
pc[j] = Q[i].c[j];
poff += pc[j] * pstr[j];
}
upd(0, 0, 0);
}
unsigned long long ans = 0;
for (int i = 0; i < dpsz; ++i) {
ans += dp[i];
}
ans %= MOD;
write(ans);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t; read(t);
while (t--) solve();
return 0;
}