【UNR #2】黎明前的巧克力 (组合数学)(生成函数)(FWT)

本文探讨了一种算法问题,即寻找两个集合的异或值相等的所有可能划分,提出了一种巧妙的解决方案,通过生成函数和快速沃尔什-赫达曼变换(FWT)来高效计算方案数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:求出两个集合使他们异或值相同,问方案数

考虑统计那些异或起来为 0 的集合,这样的集合的任何一个划分都是合法的

一个大小为 ∣ S ∣ |S| S 的集合的贡献是 2 ∣ S ∣ 2^{|S|} 2S

我们考虑上生成函数然后 F W T FWT FWT

得到 ∏ ( 2 ∗ x a i + 1 ) \prod(2*x^{a_i}+1) (2xai+1) 过后 i f w t ifwt ifwt 回去,第 0 0 0 项的值就是答案

发现如果对每一行都做一遍 f w t fwt fwt 太浪费了

能不能对所有的一起做呢?

考虑 f w t fwt fwt 的过程, 1 1 1 对所有位置有贡献, 2 2 2 对一些位置有 − 2 -2 2 的贡献,一些有 2 2 2 的贡献,所以最后的值一定是 − 1 , 3 -1,3 1,3

我们现在需要知道的是一列的变换完后的 ∏ \prod ,发现只需要知道其中 -1 和 3 的个数

注意到 f w t fwt fwt 的和等于和的 f w t fwt fwt,将所有的合起来做一遍 f w t fwt fwt 就可以解出个数

挺巧妙的,一个是直接写出一个 f w t fwt fwt 形式的生成函数
二个是把所有的合起来做一个 f w t fwt fwt,而不是一个一个做

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e6 + 5, M = 1 << 21 | 5;
cs int Mod = 998244353, inv2 = (Mod+1)/2;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Mul(int &a, int b){ a = mul(a, b); }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans, a); return ans;}
cs int inv4 = mul(inv2, inv2);
int n, a[M], pw[N], MAX, deg; 
void FWT(int *a, int typ){
	for(int i = 1; i < deg; i <<= 1)
	for(int j = 0; j < deg; j += (i<<1))
	for(int k = 0; k < i; k++){
		int x = a[k + j], y = a[k + j + i];
		a[k + j] = add(x, y); a[k + j + i] = dec(x, y);
		if(typ == -1) Mul(a[k + j], inv2), Mul(a[k + j + i], inv2);
	}
}
int main(){
	scanf("%d", &n); a[0] = n;
	for(int i = 1; i <= n; i++){ int x; scanf("%d", &x); MAX = max(MAX, x); a[x] += 2; }
	pw[0] = 1; for(int i = 1; i <= n; i++) pw[i] = mul(pw[i-1], 3);
	deg = 1; while(deg <= MAX) deg <<= 1;
	FWT(a, 1);
	for(int i = 0; i < deg; i++){
		int ct3 = mul(add(a[i], n), inv4), ct1 = n - ct3;
		a[i] = (ct1&1) ? dec(0,pw[ct3]) : pw[ct3]; 
	}
	FWT(a, -1); cout << dec(a[0], 1); return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值