【UNR #2】黎明前的巧克力 解题报告

【UNR #2】黎明前的巧克力

首先可以发现,等价于求 xor 和为 \(0\) 的集合个数,每个集合的划分方案数为 \(2^{|S|}\) ,其中 \(|S|\) 为集合的大小

然后可以得到一个朴素 dp ,令 \(dp_{i,j}\) 代表前 \(i\) 个数字 xor 和为 \(j\) 的集合个数

显然转移为

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

从 FWT 的角度考虑,转移其实就是每次卷上 b

\[ b_{0}=1,b_{a[i]}=2 \]

考虑异或 FWT 的正变换

\[ b'_i=\sum_{j} b_j(-1)^{popcount(i\&j)} \]

我们可以发现, \(b\) 经过正变换后,每个位置的值要么是 \(3\) ,要么是 \(-1\)

我们把每个 \(b\) 的正变换乘在一起,实际上就是把若干个 \(3\)\(-1\) 乘在一起,我们只需要球出每个位置有多少个 \(3\) 就可以了

显然有 \(cnt_3+cnt_1=n\)

然后我们有 和的 FWT 等于 FWT 的和

于是我们把原本所有的 \(b\) 加在一起做一遍 FWT 得到 \(c\),就可以得到第二个方程,对第 \(i\) 个位置

\[ cnt_3\times 3-cnt_1=c_i \]

然后我们就可以求出经过所有 \(b\) 变换的答案数组了,把它 fwt 回去即可


Code:

#include <cstdio>
#include <cctype>
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
    x=0;char c=gc();
    while(!isdigit(c)) c=gc();
    while(isdigit(c)) x=x*10+c-'0',c=gc();
}
const int mod=998244353;
const int inv2=499122177;
#define mul(x,y) (1ll*(x)*(y)%mod)
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int qp(int d,int k)
{
    int f=1;
    while(k)
    {
        if(k&1) f=mul(f,d);
        d=mul(d,d);
        k>>=1;
    }
    return f;
}
const int N=1<<20;
int n,lim=1,a[N];
void FWT(int *a,int lim,int typ)
{
    for(int le=1;le<lim;le<<=1)
        for(int i=0;i<lim;i+=le<<1)
            for(int j=i;j<i+le;j++)
            {
                int x=a[j],y=a[j+le];
                if(typ)
                {
                    a[j]=add(x,y);
                    a[j+le]=add(x,mod-y);
                }
                else
                {
                    a[j]=mul(add(x,y),inv2);
                    a[j+le]=mul(add(x,mod-y),inv2);
                }
            }
}
int main()
{
    read(n);
    for(int x,i=1;i<=n;i++)
    {
        read(x);
        ++a[0],a[x]+=2;
        while(lim<=x) lim<<=1;
    }   
    FWT(a,lim,1);
    for(int i=0;i<lim;i++)
    {
        int cnt3=mul(a[i]+n,748683265);
        //printf("%d %d\n",a[i]+n,cnt3);
        int cnt1=add(n,mod-cnt3);
        a[i]=qp(3,cnt3);
        if(cnt1&1) a[i]=mod-a[i];
    }
    //for(int i=0;i<lim;i++) printf("%d ",a[i]);puts("");
    FWT(a,lim,0);
    printf("%d\n",add(a[0],mod-1));
    return 0;
}

2019.7.8

转载于:https://www.cnblogs.com/butterflydew/p/11149824.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值