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

题目描述:

uoj

题解:

WTF。

看题解看了一个小时才看明白。

首先有状态$f[i][j]$表示前$i$个东西两人取,最后两人异或和为$j$的有多少方案。

转移为$f[i][j]=f[i-1][j]+2*f[i-1][j \oplus a[i]]$。

显然跑FWT做异或卷积(显然会T)。

发现卷积中每次卷的是{1,0,0,……,0,2,0……}这样一个东西。

打表发现FWT后每一项是-1或3。

其实很好解释,从贡献的角度讲,0位的贡献都是1,而$a[i]$位的贡献是2或-2,所以是3或-1。

考虑将所有的$a[i]$放在一起做FWT。

这样的话每组对每一位上的贡献是-1或3,共有$n$组,贡献和为$s$。

设贡献为$3$的有$x$组,那么$3x-(n-x)=s$,有$x=\frac{n+s}{4}$。

然后快速幂再卷回去就好了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 2000050;
const int MOD = 998244353;
const int inv_2 = (MOD+1)/2;
const int inv_4 = 1ll*inv_2*inv_2%MOD;
template<typename T>
inline void read(T&x)
{
    T f = 1,c = 0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
    x = f*c;
}
template<typename T>inline void Mod(T&x){if(x>=MOD)x-=MOD;}
ll fastpow(ll x,int y)
{
    ll ret = 1;
    while(y)
    {
        if(y&1)ret=ret*x%MOD;
        x=x*x%MOD;y>>=1;
    }
    return ret;
}
int n,a[N],lim;
void fwt(int*a,int len,int k)
{
    for(int i=1;i<len;i<<=1)
        for(int j=0;j<len;j+=(i<<1))
            for(int o=0;o<i;o++)
            {
                int w1 = a[j+o],w2 = a[j+o+i];
                Mod(a[j+o] = w1+w2),Mod(a[j+o+i]=w1+MOD-w2);
                if(k==-1)a[j+o]=1ll*a[j+o]*inv_2%MOD,a[j+o+i]=1ll*a[j+o+i]*inv_2%MOD;
            }
}
int main()
{
    read(n);int mx=0;
    for(int i=1,x;i<=n;i++)
    {
        read(x);
        if(x>mx)mx=x;
        a[0]++,a[x]+=2;
    }
    lim = 1;
    while(lim<=mx)lim<<=1;
    fwt(a,lim,1);
    for(int i=0;i<lim;i++)
    {
        int now = 1ll*(n+a[i])*inv_4%MOD;
        a[i] = fastpow(3,now);
        if((n-now)&1)a[i]=MOD-a[i];
    }
    fwt(a,lim,-1);
    printf("%d\n",(a[0]+MOD-1)%MOD);
    return 0;
}
View Code

 

posted @ 2019-07-08 23:18  LiGuanlin  阅读(...)  评论(...编辑  收藏