UOJ#310.【UNR #2】黎明前的巧克力(FWT)

题意

给出 n 个数 {a1,,an},从中选出两个互不相交的集合(不能都为空),使得第一个集合与第二个集合内的数的异或和相等,求总方案数 mod998244353

n,ai106

题解

简单转化一下,其实就是对于每个选取集合中元素异或积为 0 的集合,都会有 2|S| 的贡献。

用集合幂级数形式写出来其实就等价于:

i=1n(1+2xai)

把每个 FWT 再乘显然不现实。观察一下 1+2xai FWT 后的点值只可能是 {1,3}

这样我们把所有原来的幂级数相加,然后一起 FWT 。(因为 FWT 是可以满足点值上的加减乘除,与集合对称差卷积上的加减乘除是一样的)

然后每一位算一下,假设有 x1nx3 。考虑解一下这个方程,也就是 x+3(nx)=fi ,也就是 x=3nfi4

那么所有一开始的幂级数 FWT 乘到一起其实也就是 (1)x3nx

那么最后 IFWT 就行了。

最后要减去空集的贡献,注意要 + Mod - 1 (好多人被坑了 97pts )。

复杂度是 O(nlogn) 的。

代码

#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; typedef long long ll; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("310.in", "r", stdin); freopen ("310.out", "w", stdout); #endif } const int len = 1 << 20, N = len << 1, Mod = 998244353, inv2 = (Mod + 1) / 2; inline int fpm(int x, int power) { int res = 1; for (; power; power >>= 1, x = 1ll * x * x % Mod) if (power & 1) res = 1ll * res * x % Mod; return res; } void FWT(ll *P, int opt) { for (int i = 2, p = 1; i <= len; p = i, i <<= 1) for (int j = 0; j < len; j += i) Rep (k, p) { ll u = P[j + k], v = P[j + k + p]; P[j + k] = u + v; P[j + k + p] = u - v; } if (!~opt) { int inv = fpm(len, Mod - 2); Rep (i, len) P[i] = 1ll * (Mod + P[i] % Mod) * inv % Mod; } } ll A[N], pow3[N]; int main () { File(); int n = read(); pow3[0] = 1; For (i, 1, n) ++ A[0], A[read()] += 2, pow3[i] = pow3[i - 1] * 3ll % Mod; FWT(A, 1); Rep (i, len) { int x = (3 * n - A[i]) / 4; A[i] = (Mod + (x & 1 ? -1 : 1) * pow3[n - x]) % Mod; } FWT(A, -1); printf ("%lld\n", (A[0] + Mod - 1) % Mod); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10542527.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(393)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示