题意
题解
好题。做了可以加深对DWT的理解。
先把题目要求的稍微转换一下,可以发现若我们找到一个异或和为
0
的集合
所以我们考虑
dp(i,j)=dp(i−1,j)+dp(i−1,j xor a[i])∗2
。
这样复杂度太高了,考虑优化。
观察发现,可以和
XOR
卷积联系起来,对于每个
a[i]
,我们构造数组
Fi
,其中
Fi(0)=1, Fi(a[i])=2
。这样我们把
n
个数组卷起来就是答案了。
我们设
这样就可以拿到……0分了……还不如上面直接DP。
但不要放弃思路,之所以复杂度不理想,是因为每个
F
只有
作为新时代的OI少年,我们一定要把这个FWT优化到极致:
我们现在要求的是
ANS=DWT(F1)∗DWT(F2)∗...∗DWT(Fn)
。
最后把
ANS
逆变换一下就是答案。
考虑
DWT(Fi)
中的位置
0
和位置
因为 DWT 是线性变换,所以可以分开考虑。
对于位置 0 上的
对于位置 a[i] 上的 2 ,对第k位都加了
所以得到 DWT(Fi)j=1+f(j,a[i])∗2
所以 ANSi=(1+f(i,a[1])∗2)∗(1+f(i,a[2])∗2)∗...∗(1+f(i,a[n])∗2) 。
(1+f(i,a[j])∗2) 这个东西只会等于 3 或
如何才能知道呢?乘起来难求,加起来好求。
DWT(F1)+DWT(F2)+...+DWT(Fn)=DWT(F1+F2+...+Fn)
把 F 全部加到一起,做一遍
对于 Ans 的每位,若我们求出了这些数的和为 S ,那么解个方程,
设
妙啊……
复杂度 O(mlogm)
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL maxn=1100005, MOD=998244353, Inv=(MOD+1)/2;
inline char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
char ch=gc(); int res=0;
while(!('0'<=ch&&ch<='9')) ch=gc();
while('0'<=ch&&ch<='9') res=(res<<3)+(res<<1)+ch-'0', ch=gc();
return res;
}
int n,m,a[maxn];
void FWT(int a[],int n,int _k){
for(int m=2;m<=n;m<<=1)
for(int i=0;i<=n-1;i+=m)
for(int j=0;j<=m/2-1;j++){
int t0=a[i+j], t1=a[i+j+m/2];
if(_k==1) a[i+j]=t0+t1, a[i+j+m/2]=t0-t1;
else a[i+j]=(LL)(t0+t1)*Inv%MOD, a[i+j+m/2]=(LL)(t0-t1+MOD)%MOD*Inv%MOD;
}
}
LL Pow(int a,int b){
LL res=1;
for(LL w=a%MOD;b;b>>=1,w=w*w%MOD) if(b&1) res=(res*w)%MOD;
return res;
}
int main(){
freopen("uoj310.in","r",stdin);
freopen("uoj310.out","w",stdout);
n=getint();
for(int i=1;i<=n;i++){
int x=getint();
a[x]+=2; a[0]++; m=x>m?x:m;
}
int _m=1; while(_m<=m) _m<<=1; m=_m;
FWT(a,m,1);
for(int i=0;i<=m-1;i++){
a[i]=Pow(3,(a[i]+n)>>2)*( ((3*n-a[i]>>2)&1)?MOD-1:1 )%MOD;
}
FWT(a,m,-1);
printf("%d\n",(a[0]-1+MOD)%MOD);
return 0;
}