UOJ#310-[UNR #2]黎明前的巧克力【FWT】

1# 正题
题目链接:https://uoj.ac/problem/310


题目大意

给出一个长度为\(n\)的序列,求有多少种方案找出两个集合\(S,T\)使得这两个集合的异或和相等。

\(1\leq n\leq 10^6\)


解题思路

可以转换为找到一个异或和为\(0\)的集合\(S\),产生\(2^{|S|}\)的方案数。

然后考虑\(FWT\),那么我们就是要求\(\prod FWT(1+2x^{a_i})\)

这个东西比较难搞,我们考虑\(FWT(1+2x^{a_i})\)的性质,\(FWT(1+2x^{a_i})=FWT(1)+FWT(2x^{a_i})\),然后\(FWT(1)\)全是\(1\)\(FWT(2x^{a_i})\)的话就有的位置是\(2\)有的位置是\(-2\),那么最后\(FWT(1+2x^{a_i})\)出来的只会有\(3\)\(-1\)

那么如果我们求出\(FWT(\sum_{i=1}^n1+2x^{a_i})\),这样我们就能求出所有\(FWT\)的每一位值的和,又因为总数是\(n\),我们又知道\(3\)\(-1\)的和,那我们就能解出\(3\)\(-1\)的数量,然后就能求出\(\prod FWT(1+2x^{a_i})\),再\(IFWT\)就好了。

时间复杂度:\(O(n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1<<20,P=998244353;
ll n,m,a[N];
void FWT(ll *f,ll op){
	for(ll p=2;p<=n;p<<=1)
		for(ll k=0,len=p>>1;k<n;k+=p)
			for(ll i=k;i<k+len;i++){
				ll x=f[i],y=f[i+len];
				f[i]=(x+y)*op%P;
				f[i+len]=(x-y+P)*op%P;
			}
	return;
}
ll power(ll x,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
signed main()
{
	scanf("%lld",&m);n=1<<20;
	for(ll i=1,x;i<=m;i++)
		scanf("%lld",&x),a[0]++,a[x]+=2;
	FWT(a,1);
	for(ll i=0;i<n;i++){
		ll _1=0,_3=0;
		_3=(a[i]+m)*power(4,P-2)%P;_1=m-_3;
		a[i]=power(3,_3)*((_1&1)?1:(P-1))%P;
	}
	FWT(a,(P+1)/2);
	printf("%lld\n",P-a[0]-1);
	return 0;
}
posted @ 2022-05-31 00:50  QuantAsk  阅读(21)  评论(0编辑  收藏  举报