【可持久化Trie+堆】LOJ3048 [十二省联考 2019] 异或粽子

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/89260761

【题目】
LOJ
给定一个序列 a i a_i ,一个区间的价值是其 a i a_i 异或值。选择不同的 k k 个区间使得价值之和最大。
n 5 × 1 0 5 , k 2 × 1 0 5 , a i < 2 3 2 n\leq 5\times 10^5,k\leq 2\times 10^5,a_i<2^32

【解题思路】
据说是大原题。

只求一个区间最大异或值我们可以通过计算前缀异或值,丢到 Trie \text{Trie} 简单得到。
现在虽然不仅要求不同,而且要求 k k 个,但其实并不难。

考虑到 k k 并不大,我们不妨依次取出所有的最值。一种可行的思路是维护一个堆,堆里面存以每个位置为右端点能得到的最大值,每次取出一个值我们就放入对应右端点的下一个值。也就是说,现在的问题就是取出以一个位置为右端点时的第 k k 大异或值。

不妨对前缀建立可持久化 Trie \text{Trie} ,每次在 Trie \text{Trie} 上二分即可。

复杂度 O ( ( n + k ) log A ) O((n+k)\log A)

【参考代码】

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

typedef long long ll;
typedef unsigned int ui;
const int N=5e5+10,M=N*33;

ui read()
{
	ui ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

namespace Trie
{
	int rt[N];
	ui fc[33];
	struct Trie
	{
		int sz,cnt[M],ch[M][2];
		void insert(int x,int y,ui num)
		{
			for(int i=31;~i;--i)
			{
				int now=(num&fc[i])?1:0;
				ch[x][now^1]=ch[y][now^1];
				ch[x][now]=++sz;cnt[sz]=cnt[ch[y][now]]+1;
				x=ch[x][now];y=ch[y][now];
			}
		}
		ui query(int x,int k,ui num)
		{
			ui res=0;
			for(int i=31;~i;--i)
			{
				int now=(num&fc[i])?1:0;
				if(cnt[ch[x][now^1]]>=k) res+=fc[i],x=ch[x][now^1];
				else k-=cnt[ch[x][now^1]],x=ch[x][now];
			}
			return res;
		}
	}T;
}
using namespace Trie;

namespace DreamLolita
{
	int n,K;
	ui a[N];
	ll ans;
	struct data
	{
		ui val;int k,p;
		data(ui _v=0,int _k=0,int _p=0):val(_v),k(_k),p(_p){}
		bool operator <(const data&rhs)const{return val<rhs.val;}
	};
	priority_queue<data>q;
	void solution()
	{
		fc[0]=1;for(int i=1;i<32;++i)fc[i]=fc[i-1]<<1;
		n=read();K=read();
		for(int i=1;i<=n;++i) a[i]=read()^a[i-1],rt[i]=++T.sz,T.insert(rt[i],rt[i-1],a[i-1]);
		for(int i=1;i<=n;++i) q.push(data(T.query(rt[i],1,a[i]),1,i));
		while(K--)
		{
			data x=q.top();q.pop();
			ans+=x.val;
			if(x.k<x.p) q.push(data(T.query(rt[x.p],x.k+1,a[x.p]),x.k+1,x.p));
		}
		printf("%lld\n",ans);
	}
}

int main()
{
#ifdef Durant_Lee
	freopen("LOJ3048.in","r",stdin);
	freopen("LOJ3048.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/89260761