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;
}