【UOJ#310】【UNR#2】黎明前的巧克力(FWT)

【UOJ#310】【UNR#2】黎明前的巧克力(FWT)

题面

UOJ

题解

把问题转化一下,变成有多少个异或和为\(0\)的集合,然后这个集合任意拆分就是答案,所以对于一个大小为\(s\)的集合,其贡献是\(2^s\)
于是我们可以弄出若干个\((1+2x^{a_i})\)这样子的多项式,然后异或卷积把它们卷起来就是答案。
根据\(FWT\)异或卷积的理论,如果\(i\)位置有一个\(1\),那么\(FWT\)之后对于\(j\)位置的贡献是\(-1^{pop\_count(i\&j)}\)
于是\(1\)对于所有位置的贡献都是\(1\)\(2\)对于所有位置的贡献是\(\pm 2\),所以对于每一个多项式,其\(FWT\)后的结果不是\(-1\)就是\(3\)
但是对于每一个多项式分别\(FWT\)实在是太过浪费,考虑优化这个过程。
因为我们最终要求的只是每个位置上对应的所有值的乘积,每个位置上不是\(-1\)就是\(3\),那么我们假设有\(x\)\(-1\)\(n-x\)\(3\)
然后我们只需要把\(x\)给解出来就行了。
于是对应这两个值我们要找到一个等式,我们把所有的多项式加起来然后\(FWT\),这样子第\(i\)位上的值\(f_i\)就是\(n\)个多项式\(FWT\)之后的和。
于是我们有:\((-1)*x+3*(n-x)=f_i\),很容易就可以把\(x\)解出来。
然后\((-1)^x*3^{n-x}\)就是每个位置\(FWT\)乘起来之后的值。
把这个数组求出来之后再\(IFWT\)一遍就可以得到答案了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 998244353
#define inv2 499122177
#define inv4 748683265
#define MAX 1048576
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,a[MAX],pw[MAX];
void FWT(int *P,int opt,int len)
{
    for(int i=1;i<len;i<<=1)
        for(int j=0,p=i<<1;j<len;j+=p)
            for(int k=0;k<i;++k)
            {
                int X=P[j+k],Y=P[i+j+k];
                P[j+k]=(X+Y)%MOD,P[i+j+k]=(X+MOD-Y)%MOD;
                if(opt==-1)P[j+k]=1ll*P[j+k]*inv2%MOD,P[i+j+k]=1ll*P[i+j+k]*inv2%MOD;
            }
}
int main()
{
    n=read();
    pw[0]=1;for(int i=1;i<=n;++i)pw[i]=3ll*pw[i-1]%MOD;
    for(int i=1;i<=n;++i)a[0]+=1,a[read()]+=2;
    pw[0]=1;
    FWT(a,1,1048576);
    for(int i=0;i<1048576;++i)
    {
        int p=1ll*(n+n+n+MOD-a[i])*inv4%MOD;
        a[i]=(p&1)?(MOD-pw[n-p])%MOD:pw[n-p];
    }
    FWT(a,-1,1048576);
    int ans=(a[0]+MOD-1)%MOD;
    printf("%d\n",ans);
    return 0;   
}
posted @ 2019-07-11 20:01 小蒟蒻yyb 阅读(...) 评论(...) 编辑 收藏