ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【luogu CF1119H】Triple(FWT)

2022-06-03 08:00:20  阅读:177  来源: 互联网

标签:Triple luogu 个数 然后 异或 数组 FWT include


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有