1. 程式人生 > >【uoj310】[UNR #2]黎明前的巧克力 FWT

【uoj310】[UNR #2]黎明前的巧克力 FWT

ros turn dot 轉化 異或 true pos size bsp

題目描述

給出 $n$ 個數,從中選出兩個互不相交的集合,使得第一個集合與第二個集合內的數的異或和相等。求總方案數。

輸入

第一行一個正整數 $n$ ,表示巧克力的個數。
第二行 $n$ 個整數 $a_i$ 表示每個巧克力的美味值。

輸出

輸出一行一個整數,表示能使得他們心情契合的吃巧克力的方案數對 998244353 取模的結果。

樣例輸入

6
1 2 3 4 5 6

樣例輸出

80


題解

FWT

首先如果兩個集合的異或相等,那麽它們的異或為0。原問題轉化為求選出一個異或和為0的集合並分為兩個即可的方案數。

那麽設 $f[i][j]$ 表示前 $i$ 個數中選出的數的異或和為 $j$ 的方案數。那麽就有 $f[i][j]=f[i-1][j]+2·f[i-1][j\ xor\ a[i]]$ 。

可以發現這是一個異或卷積的形式,相當於每次卷的是:$b[0]=1,b[a[i]]=2$ ,然而並無卵用 = =

考慮對這個過程進行FWT,那麽:

0對每個位置的貢獻都是1;
a[i]對某些位置的貢獻是2,對某些位置的貢獻是-2。

所以每次卷上的 $b$ 數組的每個數都是-1或3。

另有:和的FWT等於FWT的和。

因此把它們求和後進行FWT,那麽就知道了每個位置FWT的和。

由於只有-1和3,因此可以解出-1和3的個數,然後快速冪處理一下即可。

最終再逆FWT回來即可。

時間復雜度 $O(n\log n)$

#include <cstdio>
#define N 1050000
#define mod 998244353
typedef long long ll;
ll a[N] , b[N];
ll pow(ll x , ll y)
{
	ll ans = 1;
	while(y)
	{
		if(y & 1) ans = ans * x % mod;
		x = x * x % mod , y >>= 1;
	}
	return ans;
}
void fwt(ll *a , int n , int flag)
{
	int i , j , k , t;
	for(i = 1 ; i < n ; i <<= 1)
		for(j = 0 ; j < n ; j += (i << 1))
			for(k = j ; k < j + i ; k ++ )
				t = a[k] , a[k] = (t + a[k + i]) * flag % mod , a[k + i] = (t - a[k + i] + mod) * flag % mod;
}
int main()
{
	int n , mx = 0 , m = 1 , i , x;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%d" , &x) , a[0] ++ , a[x] += 2;
		if(mx < x) mx = x;
	}
	while(m <= mx) m <<= 1;
	fwt(a , m , 1);
	for(i = 0 ; i < m ; i ++ )
	{
		x = (n + a[i]) * 748683265 % mod;
		if(((x + n) % mod) & 1) b[i] = (mod - pow(3 , x)) % mod;
		else b[i] = pow(3 , x);
	}
	fwt(b , m , 499122177);
	printf("%lld\n" , (b[0] - 1 + mod) % mod);
	return 0;
}

【uoj310】[UNR #2]黎明前的巧克力 FWT