【BZOJ3166】[HEOI2013] Alo(可持久化Trie树)

点此看题面

大致题意:(n)个互不相同的数,对于一段区间,定义其中次大值与区间中另一数异或的最大值为区间的价值。求最大的区间价值。

枚举次大值

考虑我们枚举次大值,那么要满足它是次大值,显然要满足区间中有且仅有一个数比它大。

于是我们在两边分别找到第二个比它大的数的位置(l,r),那么它就可以与([l+1,r-1])这一区域内任一数计算答案。(虽然这一区间左右两边各有一个比它大的数,但我们完全可以在选区间时保证恰有一个)

而如何求一个数与一段区间内的数的最大异或值,显然可持久化(Trie)啊。

怎么找到第二个比它大的数

一开始(naive)了结果就(WA)了一发。。。

其实我们可以从大到小枚举次大值,同时用(set)维护已经处理过的元素,然后只要找到前驱的前驱/后继的后继,就可以找到第二个比它大的数了。

具体实现可以详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define LV 30
using namespace std;
int n,a[N+5],s[N+5];set<int> S;
class Trie//可持久化Trie
{
	private:
		int Nt,Rt[N+5];struct node {int Sz,S[2];}O[N*(LV+2)+5];
		I void Ins(int& x,CI y,CI v,CI d)//插入
		{
			++(O[x=++Nt]=O[y]).Sz;if(!~d) return;
			RI t=(v>>d)&1;Ins(O[x].S[t],O[y].S[t],v,d-1);
		}
		I int Qry(CI x,CI y,CI v,CI d)//询问
		{
			if(!~d) return 0;RI t=(v>>d)&1^1;
			if(O[O[x].S[t]].Sz^O[O[y].S[t]].Sz) return Qry(O[x].S[t],O[y].S[t],v,d-1)|(1<<d);
			return Qry(O[x].S[t^1],O[y].S[t^1],v,d-1);
		}
	public:
		I void Ins(CI v,CI x) {Ins(Rt[v],Rt[v-1],x,LV);}
		I int Qry(CI l,CI r,CI x) {return Qry(Rt[l-1],Rt[r],x,LV);}
}P;
I bool cmp(CI x,CI y) {return a[x]<a[y];}
int main()
{
	RI i,T;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i),P.Ins(s[i]=i,a[i]);
	S.insert(-1),S.insert(0),S.insert(n+1),S.insert(n+2);//先插入几个元素防越界
	RI l,r,t,ans=0;for(sort(s+1,s+n+1,cmp),i=n;i;--i)//从大到小枚举
		l=*--(--S.lower_bound(s[i])),r=*++S.upper_bound(s[i]),S.insert(s[i]),//找前驱的前驱和后继的后继
		i^n&&(t=P.Qry(max(l+1,1),min(r-1,n),a[s[i]]),t>ans&&(ans=t));//在对应区间内找答案
	return printf("%d",ans),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3166.html