题解「CFGYM102331B Bitwise Xor」
题意
给定一个序列,求任意两个数的异或值不小于 \(x\) 的子序列数。
题解
要求任意两个数异或不小于 \(x\) 不太好做,如果只要求相邻两个数的异或值不小于 \(x\) ,可以用01Trie优化DP做。我们需要通过某种方式将任意两个数的限制转化为相邻两数的限制。
考虑一个序列,将它所有的数插入一棵Trie中,这个序列任意两个数异或值的最小值一定是Trie树上相邻两个数异或的最小值。注意到Trie树上叶节点是有序的,那么最小值一定是将这个序列排序后相邻两个数异或值的最小值。
将序列排序后做01Trie优化DP即可。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define maxn 300005
#define R register
#define INF 0x3f3f3f3f
#define mod 998244353
#define lxl long long
using namespace std;
inline lxl read()
{
lxl x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
lxl n,x,a[maxn];
int ch[maxn*60][2],tot=1;
lxl sum[maxn*60],f[maxn];
inline void insert(lxl s,lxl v)
{
int u=1;
for(R lxl i=60;i>=0;--i)
{
int c=((s>>i)&1);
if(!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
sum[u]=(sum[u]+v)%mod;
}
}
inline lxl query(lxl s)
{
lxl ans=0;int u=1;
for(R int i=60;i>=0;--i)
{
if((x>>i)&1)
u=ch[u][((s>>i)&1)==0];
else
{
ans=(ans+sum[ch[u][((s>>i)&1)==0]])%mod;
u=ch[u][((s>>i)&1)!=0];
}
}
return (ans+sum[u])%mod;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CFGYM102337B.in","r",stdin);
#endif
n=read(),x=read();
for(R int i=1;i<=n;++i)
a[i]=read();
sort(a+1,a+n+1);
for(R int i=1;i<=n;++i)
{
f[i]=(query(a[i])+1)%mod;
insert(a[i],f[i]);
}
lxl ans=0;
for(R int i=1;i<=n;++i)
ans=(ans+f[i])%mod;
printf("%lld\n",ans);
return 0;
}