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

传送门

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

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

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

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

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

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

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

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

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

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

挺巧妙的,一个是直接写出一个 f w t fwt 形式的生成函数
二个是把所有的合起来做一个 f w t 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;
}
发布了610 篇原创文章 · 获赞 94 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/103427962