【luogu CF1119H】Triple(FWT)
作者:互联网
Triple
题目链接:luogu CF1119H
题目大意
给你 n 个数组,每个数组有 n 个数,其中有 x 个 ai,y 个 bi,z 个 ci。
x,y,z 是每个数组都一样,而 ai,bi,ci 每个数组不一样。
然后问你对于每个 i,从每个数组中选一个数,它们的异或和是 i 的情况有多少种。
思路
考虑弄一个类似每个数组的生成函数
\(u\) 个 \(a_i\),\(v\) 个 \(b_i\),\(w\) 个 \(c_i\)
\(ux^{a_i}+vx^{b_i}+wx^{c_i}\)
然后答案就是它们乘起来(FWT 异或乘)得到的数组。
但是不能一个一个乘,那我们考虑像某 巧克力 那道题一样,把它们加起来,然后乘,然后同两国解方程来得到每种情况的真实个数,然后得到真实的值。
然后因为你这样有 \(8\) 种情况,比较多,我们考虑小小的简化一下,第 \(i\) 个数组的大小都异或上 \(a_i\),然后就只有 \(4\) 种情况,然后答案的位置异或上所有 \(a_i\) 的异或和即可。
\(a_i\rightarrow 1,b_i\rightarrow b_i\oplus a_i,c_i\rightarrow \oplus a_i\)
\(u+vx^{b_i}+wx^{c_i}\)
一个位置 \(i\),四个可能:
\(u+v+w\) 个数 \(n_1\)
\(u+v-w\) 个数 \(n_2\)
\(u-v+w\) 个数 \(n_3\)
\(u-v-w\) 个数 \(n_4\)
那我们考虑能不能用什么方法解方程之类的解出它们四个。
首先第一个式子显然四个 \(n_i\) 的和是 \(n\)。
那我们考虑只看 \(v\) 的正负(就是不管 \(u,w\) 是啥,亦或者当做 \(0\))
那这就是一个普通的 FWT,可以得到 \(y_1\)。
那同理我们只看 \(w\) 的正负,就可以得到 \(y_2\)。
还差一个,那我们考虑能不能考虑 \(v,w\) 同时做一个贡献。
那就是 \(b_i\oplus c_i\) 的位置是 \(1\),其它的位置是 \(0\),其实就是上面的两个式子卷积起来。
然后这么搞可以得到一个 \(y_3\),那式子就齐了。
\(\begin{cases}n_1+n_2+n_3+n_4=n\\ n_1+n_2-n_3-n_4=y_1\\ n_1-n_2+n_3-n_4=y_2\\ n_1-n_2-n_3+n_4=y_3\end{cases}\)
\(\begin{cases}n_1=(n+y_1+y_2+y_3)/4\\n_2=(n+y_1-2n_1)/2\\n_3=(n+y_2-2n_1)/2\\n_4=(n+y_3-2n_1)/2\end{cases}\)
然后这题就可以做了,不难看出对于这种题有一个模型。
就是你通过每个位置是否贡献都做一次 FWT,得到方程的常数项(一共有 \(2^k\) 种,正好对应 \(2^k\) 个可能),然后你可以高消或者自己暴力解出方程,再带回去,IFWT。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define mo 998244353
using namespace std;
const int N = (1 << 17);
int n, k, all;
ll f[N], f1[N], f2[N], f3[N], u, v, w;
ll ksm(ll x, ll y) {
ll re = 1;
while (y) {
if (y & 1) re = re * x % mo; x = x * x % mo; y >>= 1;
}
return re;
}
void FWT(ll *f, ll op, int n) {
for (int mid = 1; mid < n; mid <<= 1)
for (int R = mid << 1, j = 0; j < n; j += R)
for (int k = 0; k < mid; k++) {
ll x = f[j | k], y = f[j | mid | k];
f[j | k] = (x + y) % mo * op % mo;
f[j | mid | k] = (x - y + mo) % mo * op % mo;
}
}
int main() {
scanf("%d %d", &n, &k);
scanf("%lld %lld %lld", &u, &v, &w);
for (int i = 1; i <= n; i++) {
int a, b, c; scanf("%d %d %d", &a, &b, &c);
b ^= a; c ^= a; all ^= a;
f1[b]++; f2[c]++; f3[b ^ c]++;
}
FWT(f1, 1, 1 << k); FWT(f2, 1, 1 << k); FWT(f3, 1, 1 << k);
ll inv4 = ksm(4, mo - 2), inv2 = ksm(2, mo - 2);
for (int i = 0; i < 1 << k; i++) {
ll n1 = (n + f1[i] + f2[i] + f3[i]) % mo * inv4 % mo;
ll n2 = (n + f1[i] - 2 * n1 + 2 * mo) % mo * inv2 % mo;
ll n3 = (n + f2[i] - 2 * n1 + 2 * mo) % mo * inv2 % mo;
ll n4 = (n + f3[i] - 2 * n1 + 2 * mo) % mo * inv2 % mo;
f[i] = ksm((u + v + w) % mo, n1) * ksm((u + v - w + mo) % mo, n2) % mo
* ksm((u - v + w + mo) % mo, n3) % mo * ksm((u - v - w + 2 * mo) % mo, n4) % mo;
}
FWT(f, (mo + 1) / 2, 1 << k);
for (int i = 0; i < 1 << k; i++) printf("%lld ", f[i ^ all]);
return 0;
}
标签:Triple,luogu,个数,然后,异或,数组,FWT,include 来源: https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1119H.html