P4098 [HEOI2013]ALO
题目描述
Welcome to ALO ( Arithmetic and Logistic Online)。这是一个 VR MMORPG, 如名字所见,到处充满了数学的谜题
现在你拥有 n 颗宝石,每颗宝石有一个能量密度,记为 ai,这些宝石的能量 密度两两不同。现在你可以选取连续的一些宝石(必须多于一个)进行融合,设 为 ai, ai+1, …, aj,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值 与其他任意一颗宝石的能量密度按位异或的值,即,设该段宝石能量密度次大值 为 k,则生成的宝石的能量密度为 max{k xor ap | ap ≠ k , i ≤ p ≤ j}
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最 大
输入输出格式
输入格式:
第一行,一个整数 n,表示宝石个数
第二行,n 个整数,分别表示 a1 至 an,表示每颗宝石的能量密度,保证对于 i ≠ j 有 ai ≠ aj
输出格式:
输出一行一个整数,表示最大能生成的宝石能量密度
输入输出样例
说明
【样例解释】
选择区间[1,5],最大值为 7 xor 9
【数据规模与约定】
对于 20%的数据有 n ≤ 100
对于 50%的数据有 n ≤ 2000
对于 100%的数据有 1 ≤ n ≤ 50000, 0 ≤ ai ≤ 10^9
Solution:
本题可持久化trie树+平衡树。
题意就是随便一个长度大于1的区间,用区间中的某一值异或区间次大值,求该值最大是多少。
先用可持久化trie数维护每个权值,再考虑枚举每个数作为次大值的情况,那么我们需要确定的就是以该数作为次大值,覆盖的区间$[l,r]$最大是多少,就可以直接在trie树上贪心了。
于是解题关键就是如何快速的求每个数作为次大值,覆盖的最大区间$[l,r]$。
我们可以把数从大到小排序,然后用平衡树维护每个数的下标,初始时树为空,我们可以先往树中插入一个$0$和$n+1$作为区间限制。
按排序后的顺序枚举每个数作为次大值,对于数$a_i$,设其原下标为$k$,因为我们是按权值从大到小把下标加入平衡树,那么就可以保证此时平衡树中的下标所代表的值都大于$a_i$,而平衡树所维护的是每个数的下标,所以直接查询$k$的前驱的前驱$l$,和$k$的后继的后继$r$,那么就能确定以$a_i$为次大值的最大区间范围为$[l+1,r-1]$了。(实现时有很多细节需要注意)
代码:
/*Code by 520 -- 9.29*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) #define son(x) (x==ch[fa[x]][1]) using namespace std; const int N=50005; int n,cnt,rt[N]; struct num{ int v,id; bool operator < (const num &a) const {return v>a.v;} }a[N]; struct node{ int son[2],tot; }t[N*50]; int gi(){ int a=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar(); return a; } struct fhq_treap{ int root,cnt,date[N],ch[N][2],rnd[N],siz[N]; il void clr(){ cnt=root=0,memset(ch,0,sizeof(ch)),memset(date,0,sizeof(date)); } il int newnode(int v){ ++cnt; siz[cnt]=1,date[cnt]=v,rnd[cnt]=rand(); return cnt; } il void up(int rt){siz[rt]=siz[ch[rt][0]]+siz[ch[rt][1]]+1;} int merge(int x,int y){ if(!x||!y) return x+y; if(rnd[x]<rnd[y]) {ch[x][1]=merge(ch[x][1],y),up(x);return x;} else {ch[y][0]=merge(x,ch[y][0]),up(y);return y;} } void split(int rt,int k,int &x,int &y){ if(!rt) {x=0,y=0;return;} if(date[rt]<=k) x=rt,split(ch[rt][1],k,ch[x][1],y),up(x); else y=rt,split(ch[rt][0],k,x,ch[y][0]),up(y); } il void ins(int v){ int x=0,y=0; split(root,v,x,y); root=merge(merge(x,newnode(v)),y); } il int kth(int rt,int v){ if(!v) return 0; while(1){ if(siz[ch[rt][0]]>=v) rt=ch[rt][0]; else if(siz[ch[rt][0]]+1<v) v-=siz[ch[rt][0]]+1,rt=ch[rt][1]; else return date[rt]; } } il int pre(int v){ int x=0,y=0,ans; split(root,v-1,x,y); ans=kth(x,siz[x]); root=merge(x,y); return ans; } il int suc(int v){ int x=0,y=0,ans; split(root,v,x,y); ans=kth(y,1); root=merge(x,y); return ans; } }T; void ins(int &rt,int lst,int d,int v){ t[rt=++cnt]=t[lst],t[rt].tot++; if(d<0) return; int p=v&(1<<d)?1:0; ins(t[rt].son[p],t[lst].son[p],d-1,v); } int query(int l,int r,int d,int v,int ans){ if(d<0) return ans; int p=v&(1<<d)?1:0,ls=t[t[l].son[p^1]].tot,rs=t[t[r].son[p^1]].tot; if(rs-ls) return query(t[l].son[p^1],t[r].son[p^1],d-1,v,ans|(1<<d)); return query(t[l].son[p],t[r].son[p],d-1,v,ans); } int main(){ srand(time(0)); n=gi(); For(i,1,n) a[i].v=gi(),a[i].id=i,ins(rt[i],rt[i-1],30,a[i].v); sort(a+1,a+n+1); int l,r,lp,rp,ans=query(rt[0],rt[n],30,a[2].v,0); T.clr(); T.ins(a[1].id),T.ins(a[2].id),T.ins(n+1); For(i,3,n) { l=T.pre(a[i].id),r=T.suc(a[i].id); if(!l) l=1; else l=T.pre(l)+1; if(r>n) r=n; else r=T.suc(r)-1; if(r-l) ans=max(ans,query(rt[l-1],rt[r],30,a[i].v,0)); T.ins(a[i].id); } cout<<ans; return 0; }