CodeForces 1906K Deck-Building Game

发布时间 2023-12-25 18:41:56作者: zltzlt

洛谷传送门

CF 传送门

UNR #2 黎明前的巧克力

枚举两个人选的卡的并集 \(S\),那么当 \(\bigoplus\limits_{i \in S} a_i = 0\)\(S\) 有贡献 \(2^{|S|}\)

考虑将 \(2^{|S|}\) 分摊到每个元素上,也就是每个元素有 \(2\) 的贡献,然后把这些贡献乘起来。所以题目其实是想让我们算这个东西:\([x^0] \prod\limits_{i = 1}^n (1 + 2x^{a_i})\),定义 \(x^i \times x^j = x^{i \oplus j}\)

\(F_i(x) = 1 + 2x^{a_i}\),考虑给每个 \(F_i\) FWT 一下,对位乘后再逆 FWT 回去。因此我们已经有了一个 \(O(nV)\) 的做法,显然不能通过。

考虑 \(F_i\) FWT 后的结果,因为此处 \(\text{FWT}(a)_i = \sum\limits_{j} (-1)^{\text{popcount}(i \& j)} a_j\),容易发现 \(\text{FWT}(F_i)\) 只含有 \(1\)\(-3\)。因此我们对位乘后第 \(i\) 位的结果可以表示成 \((-1)^{x_i} 3^{n - x_i}\)

\(G = \text{FWT}(F_1) + \text{FWT}(F_2) + \cdots + \text{FWT}(F_n)\),那么我们有 \(-x_i + 3(n - x_i) = G_i\)。解得 \(x_i = \frac{3n - G_i}{4}\)。又因为 FWT 的线性性我们有 \(G = \text{FWT}(F_1 + F_2 + \cdots F_n)\),所以 \(G_i\)\(x_i\) 都能快速求出来。所以我们求出这样对位乘后第 \(i\) 位的结果,最后再逆 FWT 回去即可得到 \([x^0] \prod\limits_{i = 1}^n F_i\)

总时间复杂度 \(O(n \log V)\)

code
// Problem: K. Deck-Building Game
// Contest: Codeforces - 2023-2024 ICPC, Asia Jakarta Regional Contest (Online Mirror, Unrated, ICPC Rules, Teams Preferred)
// URL: https://codeforces.com/problemset/problem/1906/K
// Memory Limit: 1024 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 131100;
const int N = 131072;
const ll mod = 998244353;
const ll inv2 = (mod + 1) / 2;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, a[maxn];

inline void FWT(ll *a, ll o) {
	for (int k = 1; k < N; k <<= 1) {
		for (int i = 0; i < N; i += (k << 1)) {
			for (int j = 0; j < k; ++j) {
				ll x = a[i + j], y = a[i + j + k];
				a[i + j] = (x + y) * o % mod;
				a[i + j + k] = (x - y + mod) * o % mod;
			}
		}
	}
}

void solve() {
	scanf("%lld", &n);
	for (int i = 0, x; i < n; ++i) {
		scanf("%d", &x);
		a[x] += 2;
	}
	a[0] += n;
	FWT(a, 1);
	ll inv4 = qpow(4, mod - 2);
	for (int i = 0; i < N; ++i) {
		ll x = (n * 3 - a[i] + mod) * inv4 % mod;
		a[i] = ((x & 1) ? mod - 1 : 1) * qpow(3, n - x) % mod;
	}
	FWT(a, inv2);
	printf("%lld\n", a[0]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}