洛谷5283 十二省联考2019 异或粽子 可持久化trie 堆 贪心

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/89157870

题目链接

题意:
给你一个长度为 n n 的序列,一个区间的权值为区间的异或和。给你一个 k k ,问你所有的区间中权值前 k k 大的权值和。 n < = 2 e 5 , k < = m i n ( n ( n 1 ) 2 , 2 e 5 ) n<=2e5,k<=min(\frac{n(n-1)}{2},2e5)

题解:
送我退役的题之一。当场不知道为什么没有去好好想这个本来应该挺可做的题。

这个题有好几种做法吧。先说说官方给出的做法。先预处理一个前缀异或和,这样就可以快速求出一个区间的异或值。官方给出的做法是类似超级钢琴的做法,这样思路是维护每一个点为右端点,所有左端点中和它异或最大的值是哪一个。然后把所有的值扔进一个大根堆里,每删去一个数,以这个左端点对应的左端点为中心,把区间分成左右两个区间,但是都不包括这个中心。因为删去当前的这个之后对于这个右端点,可能成为新的最大值的位置是左右两边的两个区间中的最大值。反正就是和超级钢琴那个题思路几乎一样。

但是这个题的最大是异或意义下的最大,所以我们要用一个trie树来维护这个值,具体就是在trie树上按位走,尽可能与当前位不同的去走就行了。另外要拿出一个区间的trie树,于是要写个可持久化。

然后一个和这个差不多但是可能要好写一点的做法是,我们还是用可持久化trie和堆,但是每次我们不需要记录那么多东西了,只记录当前堆顶元素的右端点是哪个和这个右端点用了前几大的,这样在trie上二分找第 k k 大即可。我这份代码是写的这个做法。

另外还有一种更简单的做法,是当场选手想出来的。直接建出整个trie树,然后一开始对于每个点为区间的某一个端点查询最大异或值,扔进堆里。这样的问题是可能一个区间被算两次,我们发现其实把 k k 变成原来的两倍,然后最终答案除以二就可以了,因为前 k k 大的一定会都出现两次。

这样就讲完了所有的做法。时间复杂度都是 O ( n l o g n ) O(nlogn) ,写可持久化trie的空间复杂度是 O ( n l o g n ) O(nlogn) ,不可持久化的是 O ( n ) O(n)

代码:

#include <bits/stdc++.h>
using namespace std;

int n,k,ji[64],root[500010],cnt,shu[500010];
long long a[500010],s[500010],ans;
struct trie
{
	int vis[2],sz;
}tr[30000010];
priority_queue<pair<long long,int> > q;
inline long long read()
{
	long long x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline void insert(int &rt,int rt1,int len)
{	
	rt=++cnt;
	tr[rt]=tr[rt1];
	tr[rt].sz++;
	if(len<0)
	return;
	if(ji[len])
	insert(tr[rt].vis[1],tr[rt1].vis[1],len-1);
	else
	insert(tr[rt].vis[0],tr[rt1].vis[0],len-1);
}
inline long long query(int rt,long long len,int k)
{
	if(len<0)
	return 0;
	if(ji[len])
	{
		if(tr[rt].vis[0]&&k<=tr[tr[rt].vis[0]].sz)
		return query(tr[rt].vis[0],len-1,k);
		else
		return query(tr[rt].vis[1],len-1,k-tr[tr[rt].vis[0]].sz)+(1ll<<len);
	}
	else
	{
		if(tr[rt].vis[1]&&k<=tr[tr[rt].vis[1]].sz)
		return query(tr[rt].vis[1],len-1,k)+(1ll<<len);
		else
		return query(tr[rt].vis[0],len-1,k-tr[tr[rt].vis[1]].sz);
	}
}
int main()
{
	n=read();
	k=read();
	insert(root[0],root[0],34);
	for(int i=1;i<=n;++i)
	{
		shu[i]=1;
		a[i]=read();
		s[i]=s[i-1]^a[i];
		for(long long j=34;j>=0;--j)
		{
			if(s[i]&(1ll<<j))
			ji[j]=1;
			else
			ji[j]=0;
		}
		insert(root[i],root[i-1],34);
		long long qwq=query(root[i-1],34,shu[i]);
		q.push(make_pair(qwq^s[i],i));
	}
	while(k)
	{
		--k;
		pair<long long,int> qwq=q.top();
		q.pop();
		ans+=qwq.first;
		++shu[qwq.second]; 
		for(long long j=34;j>=0;--j)
		{
			if(s[qwq.second]&(1ll<<j))
			ji[j]=1;
			else
			ji[j]=0;
		}
		if(shu[qwq.second]<=qwq.second)
		{
			long long qaq=query(root[qwq.second-1],34,shu[qwq.second]);
			q.push(make_pair(qaq^s[qwq.second],qwq.second));
		}	
	}
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/89157870