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))_i=\sum_{j=0} f_j\times (-1)^{\textrm{popcount}(i\& j)} \]

现在需要计算

\[\prod\textrm{FWT}(1+2x^{a_i})_j=\prod \bigg(\sum_k f_k\times (-1)^{\textrm{popcount}(i\&k)}\bigg) \]

另一边,我们注意到似乎 \(\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 ;
}
posted @ 2020-09-11 19:00  Soulist  阅读(149)  评论(0)    收藏  举报