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

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

uoj

题解时间

对非零项极少的FWT的优化。

首先有个十分好想的DP: $ f[i][j] $ 表示考虑了前 $ i $ 个且异或和为 $ j $ 的方案数,

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

可以考虑FWT,但很明显时间复杂度没有优化。

但另一方面,每层的卷积卷的都是 $ 1,0,0,...,2,0,0,... $ 的形式,

这样一来卷之后每项都是 $ -1 $ 或 $ 3 $ 。

因此大胆一点直接把所有的式子加到一起卷积,

卷完有什么用呢?

这样卷出来的结果是每层卷积相加,而正常FWT求解是每层卷积相乘。

每一位都是共计 $ n $ 个 $ -1 $ 和 $ 3 $ 加一起,所以可以求出每一位上两种数的个数。

由于知道了每一位上两种数的个数,直接每一位快速幂就能求出原来的相乘结果。

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}};
template<typename TP>inline void read(TP &tar)
{
	TP ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();}
	tar=ret*f;
}
template<typename TP,typename... Args>inline void read(TP& t,Args&... args){read(t),read(args...);}
namespace RKK
{
const int N=2000011;
const int defaultlen=1048576;
const int mo=998244353,inv2=499122177,inv4=748683265;
void doadd(int &a,int b){if((a+=b)>=mo) a-=mo;}int add(int a,int b){return (a+=b)>=mo?a-mo:a;}
void domul(int &a,int b){a=1ll*a*b%mo;}int mul(int a,int b){return 1ll*a*b%mo;}
int fpow(int a,int p){int ret=1;while(p){if(p&1) domul(ret,a);domul(a,a),p>>=1;}return ret;}
int n,a[N];
void fwtxor(int *a,int len,int tp)
{
	for(int i=1,w1,w2;i<len;i<<=1)
	for(int j=0;j<len;j+=i<<1)
	for(int k=0;k<i;k++)
	{
		w1=a[j+k],w2=a[j+k+i];
		a[j+k]=add(w1,w2),a[j+k+i]=add(w1,mo-w2);
		if(tp==-1) domul(a[j+k],inv2),domul(a[j+k+i],inv2);
	}
}
int main()
{
	read(n);for(int i=1,w;i<=n;i++) read(w),a[0]+=1,a[w]+=2;
	fwtxor(a,defaultlen,1);
	for(int i=0,cnt1,cnt3;i<defaultlen;i++)
	{
		cnt3=mul(add(n,a[i]),inv4),cnt1=n-cnt3;
		a[i]=(cnt1&1)?mo-fpow(3,cnt3):fpow(3,cnt3);
	}
	fwtxor(a,defaultlen,-1);
	printf("%d\n",add(a[0],mo-1));
	return 0;
}
}
int main(){return RKK::main();}
posted @ 2020-07-24 10:04  RikukiIX  阅读(197)  评论(0编辑  收藏  举报