UNR2 黎明前的巧克力
C 黎明前的巧克力 [* hard]
给定数列 \(a\),长度为 \(n\),保证 \(n,a_i\le 10^6\),求有多少种方案选出两个集合 \(A,B\) 使得两个集合的异或和相同,不能均为空集,答案对 \(998244353\) 取模。
\(\rm Sol:\)
先考虑 \(A+B\) 的异或和,必然是 \(0\)
然后考虑 \(A+B\) 的大小为 \(k\),那么方案数为 \(2^k\)
问题等价于求有 \(n\) 个元素,每个元素可以选/不选,对于 \(i=1,2\sim n\) 求有多少种方案选出 \(i\) 个数使得权值异或和为 \(0\)
事实上可以考虑将每个元素视为 \((1+2x^{a_i})\) 这样的一个 Xor 多项式,这样的话 \({\oplus}_{i=1}^n (1+2x^{a_i})\) 中 \(x^0\) 就是答案了。
然后我们计算 Xor FWT,问题计算的本质上也是卷积结果,不妨考虑 \(1+2x^{a_i}\) 这个向量 \((F(x))\) ?的 Xor_FWT 结果是啥。
我们记得 \(\rm Xor ~FWT\) 的矩阵为 \(c(0,0),c(1,0),c(0,1)=1,c(1,1)=-1\),所以 FWT 理论告诉我们:
现在需要计算
另一边,我们注意到似乎 \(\textrm{FWT}(F(x))_j\) 的值只有 \(-1\) 和 \(3\) 两种取值。
同时,我们只需要知道 \(\prod \textrm{FWT}(F(x))_j\) 即可
注意到每个位置不是 \(-1\) 就是 \(3\),那么假设有 \(x\) 个 \(-1\) 那么就有 \((n-x)\) 个 \(3\)
另一边,我们注意到 \(\sum \textrm{FWT}(F(x))=\textrm{FWT}(\sum F(x))\),假设对于 \(i\) 而言,结果为 \(f_i\),那么有 \(-x+(n-x)3=f_i\to 3n-4x=f_i\) 于是就可以把 \(x\) 解出来。
然后就可以快乐的 IFWT 了。
代码极其好写
\(Code:\)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int long long
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 2e6 + 5 ;
const int P = 998244353 ;
const int I = 499122177 ;
int n, m, limit, A[N], c[N], fac[N] ;
void FWT_xor( int *a, int type ) {
for( re int k = 1; k < limit; k <<= 1 )
for( re int i = 0; i < limit; i += ( k << 1 ) )
for( re int j = i; j < i + k; ++ j ) {
int nx = a[j], ny = a[j + k] ;
a[j] = ( nx + ny ) % P, a[j + k] = ( nx - ny + P ) % P ;
if( !type ) a[j] = a[j] * I % P, a[j + k] = a[j + k] * I % P ;
}
}
signed main()
{
n = gi(), limit = 1, m = 0 ;
rep( i, 1, n ) A[i] = gi(), m = max( m, A[i] ), c[A[i]] += 2 ;
while( limit <= m ) limit <<= 1 ; fac[0] = 1 ;
for( re int i = 1; i <= 2 * n; ++ i ) fac[i] = fac[i - 1] * 3 % P ;
c[0] += n, FWT_xor( c, 1 ) ;
for( re int i = 0; i < limit; ++ i ) {
int x = ( 3 * n - c[i] + P ) % P ;
x = x * I % P * I % P, A[i] = fac[n - x] ;
if( x & 1 ) A[i] = ( P - A[i] ) ;
}
FWT_xor( A, 0 ) ;
printf("%lld\n", ( P + A[0] - 1 ) % P ) ;
return 0 ;
}