CF1119H Triple 题解

Description

SK 酱送给你了一份生日礼物。礼物是 nn 个三元组 (ai,bi,ci)(a_i,b_i,c_i) 和四个正整数 x,y,z,kx,y,z,k

你利用这 nn 个三元组填充了 nn 个数组,其中第 ii 个数组中有 xxaia_iyybib_izzcic_i(所以第 ii 个数组长度为 (x+y+z)(x+y+z)

对于 i=0,1,,2k1i=0,1,\cdots,2^k-1,回答以下询问:

  • 从每个数组中选择恰好一个数,使得这些数的 xor\mathrm{xor} 和为 ii,方案数是多少?

你只需要输出方案数对 998,244,353998,244,353 取模后得到的结果。

对于 100%100\% 的数据,保证:

  • 1n1051\le n\le 10^51k171\le k\le 17
  • 0x,y,z1090\le x,y,z\le 10^9
  • 0ai,bi,ci<2k0\le a_i,b_i,c_i\lt 2^k

Solution

首先先让 bib_icic_i 都异或上 aia_i,这样就转化为了 aia_i 等于 00 的情况。

容易发现这是个 FWT 的形式,设 cnt(i)=popcount(i)cnt(i)=popcount(i)FWTk[i]FWT_k[i] 表示 (ak,bk,ck)(a_k,b_k,c_k) 的 FWT 数组,那么 FWTk[i]=(1)cnt(i&ak)x+(1)cnt(i&bk)y+(1)cnt(i&ck)zFWT_k[i]=(-1)^{cnt(i\&a_k)}x+(-1)^{cnt(i\&b_k)}y+(-1)^{cnt(i\&c_k)}z

由于 ak=0a_k=0,所以第一项为 xx,即 FWTk[i]=x+(1)cnt(i&bk)y+(1)cnt(i&ck)zFWT_k[i]=x+(-1)^{cnt(i\&b_k)}y+(-1)^{cnt(i\&c_k)}z

这里的 FWTk[i]FWT_k[i] 只有 44 种可能,分别是 x+y+z,x+yz,xy+z,xyzx+y+z,x+y-z,x-y+z,x-y-z,设这四种分别出现了 c1,c2,c3,c4c_1,c_2,c_3,c_4 次。考虑用解方程的方式解出这四个数。

第一个式子是 c1+c2+c3+c4=nc_1+c_2+c_3+c_4=n

先设 f1,k[i]=(1)cnt(i&bk)f_{1,k}[i]=(-1)^{cnt(i\&b_k)},则 c1+c2c3c4=k=1nf1,k[i]c_1+c_2-c_3-c_4=\sum_{k=1}^{n}{f_{1,k}[i]}

同理设 f2,k[i]=(1)cnt(i&ck)f_{2,k}[i]=(-1)^{cnt(i\&c_k)},则 c1c2+c3c4=k=1nf2,k[i]c_1-c_2+c_3-c_4=\sum_{k=1}^{n}{f_{2,k}[i]}

然后设 f3,k[i]=(1)cnt(i&bk)+cnt(i&ck)f_{3,k}[i]=(-1)^{cnt(i\&b_k)+cnt(i\&c_k)},则 c1c2c3+c4=k=1nf3,k[i]c_1-c_2-c_3+c_4=\sum_{k=1}^{n}{f_{3,k}[i]}

可以用 FWT 分别求出 f1,k[i],f2,k[i],f3,k[i]f_{1,k}[i],f_{2,k}[i],f_{3,k}[i] 的和,乘起来后再 FWT 回去就是答案。

时间复杂度:O(n+2kk)O(n+2^kk)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e5 + 5, kMaxS = (1 << 17), kMod = 998244353, kInv2 = (kMod + 1) / 2;

int n, k, x, y, z, sum;
int a[kMaxN], b[kMaxN], c[kMaxN];
int f[kMaxS], f1[kMaxS], f2[kMaxS], f3[kMaxS];

constexpr int qpow(int bs, int64_t idx = kMod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
if (idx & 1)
ret = (int64_t)ret * bs % kMod;
return ret;
}

inline int fix(int x) { return (x % kMod + kMod) % kMod; }
inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

void FWT(int *a, int n) {
for (int len = 2; len <= n; len <<= 1) {
int m = len / 2;
for (int i = 0; i < n; i += len) {
for (int j = i; j < i + m; ++j) {
int tmp = a[j];
a[j] = add(a[j], a[j + m]);
a[j + m] = sub(tmp, a[j + m]);
}
}
}
}

void IFWT(int *a, int n) {
for (int len = 2; len <= n; len <<= 1) {
int m = len / 2;
for (int i = 0; i < n; i += len) {
for (int j = i; j < i + m; ++j) {
int tmp = a[j];
a[j] = 1ll * kInv2 * add(a[j], a[j + m]) % kMod;
a[j + m] = 1ll * kInv2 * sub(tmp, a[j + m]) % kMod;
}
}
}
}

void dickdreamer() {
std::cin >> n >> k >> x >> y >> z;
for (int i = 1; i <= n; ++i) {
std::cin >> a[i] >> b[i] >> c[i];
sum ^= a[i], b[i] ^= a[i], c[i] ^= a[i];
++f1[b[i]], ++f2[c[i]], ++f3[b[i] ^ c[i]];
}
FWT(f1, (1 << k)), FWT(f2, (1 << k)), FWT(f3, (1 << k));
for (int i = 0; i < (1 << k); ++i) {
int c1, c2, c3, c4;
c1 = fix(n + f1[i] + f2[i] + f3[i]) / 4;
c2 = fix(n + f1[i] - f2[i] - f3[i]) / 4;
c3 = fix(n - f1[i] + f2[i] - f3[i]) / 4;
c4 = fix(n - f1[i] - f2[i] + f3[i]) / 4;
f[i] = 1;
f[i] = 1ll * f[i] * qpow(fix(x + y + z), c1) % kMod;
f[i] = 1ll * f[i] * qpow(fix(x + y - z), c2) % kMod;
f[i] = 1ll * f[i] * qpow(fix(x - y + z), c3) % kMod;
f[i] = 1ll * f[i] * qpow(fix(x - y - z), c4) % kMod;
}
IFWT(f, (1 << k));
for (int i = 0; i < (1 << k); ++i) std::cout << f[i ^ sum] << ' ';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1119H Triple 题解
https://sobaliuziao.github.io/2024/09/25/post/20d1d0c7.html
作者
Egg_laying_master
发布于
2024年9月25日
许可协议