[UOJ UNR#2 黎明前的巧克力]

来自FallDream的博客,未经允许,请勿转载,谢谢。


传送门

很奇妙的一道题

首先不难发现一个暴力做法,就是f[i]表示异或和为i的答案数,每次FWT上一个F数组,其中F[0]=1,F[ai]=2,最后输出f[0]即可。

这样我就考虑从FWT之后的数组入手。

首先发现F[0]=1只会让最后的数组全部+1,所以只考虑F[ai]=2的影响。

发现每个项只会是3或者-1,这取决于FWT过程中的取反次数。

所以可以设计一个dp,f[i][x]表示分治到第i层,x是2的方案数,F[i][x]表示....,x是-2的方案数。

这样模拟FWT进行dp即可,最后通过快速幂计算出变换后最终的数组,再逆变换回去就是答案啦。

#include<iostream>
#include<cstring>
#include<cstdio>
#define MN 1048576
#define mod 998244353
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
const int Inv2=(mod+1)/2;
int s[MN+5],S[MN+5],n,f[22][MN+5],F[22][MN+5],num[MN+5],sum;

inline int pow(int x,int k)
{
    for(sum=1;k;k>>=1,x=1LL*x*x%mod)
        if(k&1) sum=1LL*sum*x%mod;
    return sum;
}

void FWT(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;FWT(l,mid);FWT(mid+1,r);
    for(int i=0;i<=mid-l;++i)
    {
        int x=s[l+i],y=s[mid+1+i];
        s[l+i]=1LL*(x+y)*Inv2%mod;
        s[mid+1+i]=1LL*(x-y+mod)*Inv2%mod;
    }
}

void Solve(int l,int r,int dep)
{
    if(l==r){f[dep][l]=num[l];return;}
    int mid=l+r>>1;Solve(l,mid,dep+1);Solve(mid+1,r,dep+1);
    for(int i=0;i<=mid-l;++i)
    {
        f[dep][l+i]=f[dep+1][l+i]+f[dep+1][mid+1+i];
        F[dep][l+i]=F[dep+1][l+i]+F[dep+1][mid+1+i];
        f[dep][mid+1+i]=f[dep+1][l+i]+F[dep+1][mid+1+i];
        F[dep][mid+1+i]=F[dep+1][l+i]+f[dep+1][mid+1+i];
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;++i) ++num[read()];
    Solve(0,MN-1,1);
    for(int i=0;i<MN;++i)
    {
        s[i]=pow(3,f[1][i]);
        if(F[1][i]&1) s[i]=(mod-s[i])%mod;
    }
    FWT(0,MN-1);
    printf("%d
",(s[0]-1+mod)%mod);
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/uoj310.html