Loading

题解「CFGYM102331B Bitwise Xor」

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;
}
posted @ 2020-11-15 19:21  GoPoux  阅读(221)  评论(0编辑  收藏  举报