C 黎明前的巧克力 [* hard]
给定数列 (a),长度为 (n),保证 (n,a_ile 10^6),求有多少种方案选出两个集合 (A,B) 使得两个集合的异或和相同,不能均为空集,答案对 (998244353) 取模。
( m Sol:)
先考虑 (A+B) 的异或和,必然是 (0)
然后考虑 (A+B) 的大小为 (k),那么方案数为 (2^k)
问题等价于求有 (n) 个元素,每个元素可以选/不选,对于 (i=1,2sim 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 结果是啥。
我们记得 ( m Xor ~FWT) 的矩阵为 (c(0,0),c(1,0),c(0,1)=1,c(1,1)=-1),所以 FWT 理论告诉我们:
现在需要计算
另一边,我们注意到似乎 ( extrm{FWT}(F(x))_j) 的值只有 (-1) 和 (3) 两种取值。
同时,我们只需要知道 (prod extrm{FWT}(F(x))_j) 即可
注意到每个位置不是 (-1) 就是 (3),那么假设有 (x) 个 (-1) 那么就有 ((n-x)) 个 (3)
另一边,我们注意到 (sum extrm{FWT}(F(x))= extrm{FWT}(sum F(x))),假设对于 (i) 而言,结果为 (f_i),那么有 (-x+(n-x)3=f_i o 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
", ( P + A[0] - 1 ) % P ) ;
return 0 ;
}