一直感觉枚举子集这种没啥可优化的,直到遇到这题,这题对时间卡的比较死,枚举子集其实是可以写的,但是双层 f o r for for 的枚举子集的复杂度是 2 n 2^n 2n ∗ * ∗ n n n,这题是 T = 100 T=100 T=100 组输入, n < = 20 n<=20 n<=20,然后我们可以推出贡献可以由枚举子集得到,但是这样最坏的时间复杂度是 O O O( 2 2 2 2 ^2 2 0 ^0 0 ∗ 20 *20 ∗20 ∗ 100 *100 ∗100) > O ( 3 e 8 ) O(3e8) O(3e8),所以我们可以用完全二叉树形式的 d f s ( ) dfs( ) dfs() 来优化二进制的枚举子集,这个东西还是今天一个老师在课堂上问我 b f s ( ) bfs() bfs()形式的 01 01 01 背包,我所学会的,显然 b f s ( ) bfs( ) bfs() 或者 d f s ( ) dfs( ) dfs() 的 01 01 01 背包的实质就是枚举子集。
参考代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int mod = 998244353;
void read(int& v) {
int k = 1;
v = 0;
int c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
k = 0;
c = getchar();
}
while (c >= '0' && c <= '9')
v = (v << 3) + (v << 1) + (c - 48), c = getchar();
if (k == 0)
v = -v;
}
inline ll ksm(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1)
res = res * a % mod;
b >>= 1, a = a * a % mod;
}
return res;
}
int pn[25], ps[25];
int pm[25], pos[(maxn + 50000) * 4];
void init() {
for (int i = 1; i <= 1048576; i++)
pos[i] = log2(i) + 1;
}
ll ans;
inline void dfs(int k, int l, int r, int w, ll sum) {
int i = pos[k];
if (l == r) {
ans = (ans + sum) % mod;
return;
}
int mid = (l + r) >> 1;
dfs(k << 1, l, mid, 0, sum);
dfs(k << 1 | 1, mid + 1, r, 1, ((sum * ps[i]) % mod) * pm[i] % mod);
}
int main() {
init();
int t;
read(t);
while (t--) {
int n;
read(n);
ll sum = 1;
for (int i = 1; i <= n; i++) {
read(pn[i]), read(ps[i]);
sum = (sum * ksm(pn[i], ps[i])) % mod;
pm[i] = (pn[i] - 1ll) * ksm(pn[i], mod - 2) % mod;
}
ans = 0;
dfs(1, 1, (1 << n), 0, 1);
printf("%lld\n", ans * sum % mod);
}
}