其他分享
首页 > 其他分享> > 【luogu CF1119H】Triple(FWT)

【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