HDU 5715 XOR游戏

HDU 5715 XOR游戏

题目链接:Problem - 5715


​ 看到题目描述异或和最小值最大,这种最小中的最大,最大中的最小,就是显然的二分题,我们考虑二分答案。然后利用可行性dp来check。由于异或满足结合律,我们可以先预处理出异或前缀和。然后我们先从暴力入手。我们设 \(dp_{i,j}\) 表示前 \(i\) 个数字分成 \(j\) 组满足条件的可行性。那么有一个显然的转移式:

\[dp_{i,j}=dp_{i,j}|dp_{k,j-1}(sum_i\oplus sum_k>x,k<i) \]

​ 其中 \(sum_i\) 表示 1到 \(i\) 的异或前缀和,\(x\) 表示我们目前二分的答案。

​ 这是一个 \(O(n^2)\) 的dp,显然不行,我们考虑优化。注意到只有 \(dp_{k,j-1}\) 为真且 \(sum_i\oplus sum_k>x\) 时,\(dp_{i,j}\) 的值为真。那么我们需要一个数据结构,使得能够查询 \(sum_i\) 与数据结构中的值异或的最大值。这样我们如果 \(dp_{i,j}\) 为1,我们就插入 \(sum_i\),然后查询时,查询插入的值和当前的 \(sum_i\) 异或的最大值是否大于 \(x\),如果大于,\(dp_{i,j}\) 也为真。那么这个数据结构就需要支持插入,删除,同时还要能够查询一个数与数据结构里所有数其中一个异或得到的异或和的最大值。对于异或,位运算这种类型的操作,我们可以用 Trie树 来维护。

​ Trie树的插入和查询时模板,那么删除怎么做到?其实也很简单,我们维护一个 \(cnt\) 数组,表示这个节点的权值,在插入时,我们将途经节点的权值+1,在删除时,我们将途经节点的权值-1。这样,我们在查询时,如果一个节点的权值等于0,就说明这个节点并不存在。

​ 那么时间复杂度为\(O(n\times m\times log(n)\times log(W))\)\(W\) 表示权值。常数很大,你忍一下。
代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#define R register
using namespace std;
const int MAXN = 1e4+5;
int n,m,l,a[MAXN],sum[MAXN];
bool dp[MAXN][11];
struct TRIE
{
	int to[MAXN*31][2],cnt[MAXN*31],tot;
	void insert(int num)
	{
		int a[32];
		for(int i=1;i<=31;++i)
			a[i]=num&1,num>>=1;
		int cur=0;
		for(int i=31;i>=1;--i)
		{
			if(to[cur][a[i]]==0) to[cur][a[i]]=++tot;
			cur=to[cur][a[i]];
			++cnt[cur];
		}
	}
	void del(int num)
	{
		int a[32];
		for(int i=1;i<=31;++i)
			a[i]=num&1,num>>=1;
		int cur=0;
		for(int i=31;i>=1;--i)
		{
			if(to[cur][a[i]]==0)
			{
				cout<<"wrong"<<endl;
				return ;
			}
			cur=to[cur][a[i]];
			--cnt[cur];
		}
	}
	bool find(int num,int x)
	{
		int a[32];
		for(int i=1;i<=31;++i)
			a[i]=num&1,num>>=1;
		int cur=0;int ans=0;
		for(int i=31;i>=1;--i)
		{
			if(to[cur][a[i]^1]&&cnt[to[cur][a[i]^1]])
			{
				ans|=1;
				cur=to[cur][a[i]^1];
			}
			else if(to[cur][a[i]]&&cnt[to[cur][a[i]]]) cur=to[cur][a[i]];
			else return false;
			if(ans>=x) return true;
			ans<<=1;
		}
		ans>>=1;
		return ans>=x;
	}
	void clear()
	{
		memset(cnt,0,sizeof cnt);
	}
	void empty()
	{
		memset(cnt,0,sizeof cnt);
		memset(to,0,sizeof to);
		tot=0;
	}
}trie;
bool check(int x)
{
	memset(dp,0,sizeof dp);
	dp[0][0]=1;
	for(R int j=1;j<=m;++j)
	{
		for(R int i=1;i<=n;++i)
		{
			if(dp[i-1][j-1]) trie.insert(sum[i-1]);
			if(i-l-1>=0&&dp[i-l-1][j-1]) trie.del(sum[i-l-1]);
			if(trie.find(sum[i],x))
				dp[i][j]=1;
		}
		for(int i=n-l+1;i<=n;++i)
			if(dp[i-1][j-1]) trie.del(sum[i-1]);
//		trie.clear();
	}
	return dp[n][m];
}
int main()
{
	int T,tt=0;
	scanf("%d",&T);
	while(T--)
	{
		trie.empty();
		scanf("%d %d %d",&n,&m,&l);
		for(R int i=1;i<=n;++i)
			scanf("%d",&a[i]);
		for(R int i=1;i<=n;++i)
			sum[i]=sum[i-1]^a[i];
		int l=0,r=(1ll<<30)-1,ans=-1;
		while(l<=r)
		{
			int mid=(r-l)/2+l;
			if(check(mid))
			{
				ans=mid;
				l=mid+1;
			}
			else r=mid-1;
		}
		printf("Case #%d:\n%d\n",++tt,ans);		
	}
	return 0;
}

​ 总结:数据结构优化dp,可以只往数据结构里插入可以更新后面dp值dp值从而使问题简单化。

posted @ 2021-08-08 20:34  夜空之星  阅读(50)  评论(0编辑  收藏  举报