黎明前的巧克力 题解

刚考完省选回来的时候搬的模拟赛的题。看到一道长的很类似的题所以把题解蒯过来。

首先对于每个异或和为 \(0\) 的子集 \(T\),贡献为 \(2^{|T|}\)。答案就是把他们加起来然后减掉 \(1\),是两个都不选的。

那么有一个浅显的 dp:\(dp_{i,j}\) 为考虑前 \(i\) 个元素,异或和为 \(j\) 的贡献和,那么转移:

\[dp_{i,j}=dp_{i-1,j}+dp_{i-1,j\oplus a_i} \]

若设 \(F_i\)\(dp_i\) 的集合幂级数,则有

\[F_i=F_{i-1}\times(x^{\varnothing}+2x^{a_i}) \]

\(\times\) 为异或卷积。

此时我们把答案写成了异或卷积的形式。考虑 \(\text{FWT}(x^{\varnothing}+2x^{a_i})_j\) 的值,根据定义可以得到是:

\[\text{FWT}(x^{\varnothing}+2x^{a_i})_j= \begin{cases} -1\qquad &(|a_i\cap j|\bmod 2=1)\\ 3 &(|a_i\cap j|\bmod 2=0) \end{cases} \]

那么只要算一下 \(a_i\cap j\bmod 2=1\) 的个数,随便统计一下就行了。

转化一下,设 \(cnt_i\)\(i\) 的个数,那么可以直接求 \(\sum_{i}(-1)^{i\cap j}b_j\) 然后解方程得到个数。只需要一次 FWT。

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int mod=998244353;
int n,ans,a[1<<20],pw[1<<20];
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
void fwt(int a[],int n){
    for(int mid=1;mid<n;mid<<=1){
        for(int i=0;i<n;i+=mid<<1){
            for(int j=0;j<mid;j++){
                int x=a[i+j],y=a[i+j+mid];
                a[i+j]=x+y;a[i+j+mid]=x-y;
            }
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);a[x]++;
    }
    pw[0]=1;
    for(int i=1;i<(1<<20);i++)pw[i]=3ll*pw[i-1]%mod;
    fwt(a,1<<20);
    for(int i=0;i<(1<<20);i++){
        int cnt=(a[i]+n)>>1;
        if(n-cnt&1)ans=(ans-pw[cnt]+mod)%mod;
        else ans=(ans+pw[cnt])%mod;
    }
    ans=1ll*ans*qpow(1<<20,mod-2)%mod-1+mod;
    printf("%d\n",ans%mod);
    return 0;
}
posted @ 2023-04-25 20:13  gtm1514  阅读(21)  评论(0编辑  收藏  举报