[HEOI2013]ALO-题解

题目地址

  • 题目大意

给你 n n n个互不相同的非负数,你可以选择一个区间 l , r l,r l,r,这个区间的价值为该区间的次大值异或上该区间的另外一个值的最大值,求价值最大的区间。

显然考虑一个值异或一个区间的值的最大,我们可以用可持久化trie树解决。

但是不可能 n 2 n^2 n2枚举区间,所以我们考虑对于两个次大值相同的区间 l 1 ∼ r 1 , l 2 ∼ r 2 l_1\sim r_1,l_2\sim r_2 l1r1,l2r2,如果 l 1 ∼ r 1 l_1\sim r_1 l1r1包含了 l 2 ∼ r 2 l_2\sim r_2 l2r2,那么 l 1 ∼ r 1 l_1\sim r_1 l1r1的价值显然大于等于被包含的区间的价值,所以对于每个值,我们求出当它为次大值的时候的最大区间。

考虑用主席树,我们先顺着处理 l l l,然后倒着处理 r r r,对于 l l l,就是前面比它大的值的最大位置的前面比它大的值最大位置(也就是求两次),后面同理。

比如对于 v i v_i vi,在 1 ∼ i − 1 1\sim i-1 1i1中比它大的值的最大位置为 j j j,那么在 1 ∼ j − 1 1\sim j-1 1j1中比它大的值的最大位置为 k k k,那么 v i v_i vi的左区间就是 k + 1 k+1 k+1,右区间去最小位置即可。

先预处理和建trie树,复杂度为 O ( n l o g n + n l o g v ) O(nlogn+nlogv) O(nlogn+nlogv),然后查询复杂度为 O ( n l o g v ) O(nlogv) O(nlogv)

有更简单的做法,我的做法比较麻烦

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=2e6+10,N=5e4+10,inf=1e9+7;
int n,val[N],lg,MAXV,bin[32];
int son[M][2],root[N],cnt[M],tot;
void build(int last,int &now,int v){
	if(!now)now=++tot;
	int a=now,b=last,idx;
	for(int i=lg;i>=0;i--){
		idx=(v>>i)&1;
		son[a][idx^1]=son[b][idx^1];
		cnt[son[a][idx]=++tot]=cnt[son[b][idx]]+1;
		a=son[a][idx];b=son[b][idx];
	}
}
int query(int v,int l,int r){
	if(l>r) return 0;
	int a=0,idx,L=root[l],R=root[r];
	for(int i=lg;i>=0;i--){
		idx=(v>>i)&1;
		if(cnt[son[L][idx^1]]<cnt[son[R][idx^1]]){
			a|=bin[i];
			L=son[L][idx^1];R=son[R][idx^1];
		}else{
			L=son[L][idx];R=son[R][idx];
		}
	}	return a;
}
int befmax[M],aftmax[M];
namespace Pre{
	int pos[M],ls[M],rs[M],tot,type;
	int root[N];
	void clear(){
		memset(root,0,sizeof(root));
		memset(ls,0,sizeof(ls));
		memset(rs,0,sizeof(rs));
		memset(pos,0,sizeof(pos));tot=0;
	}
	void pushup(int o){
		if(type)pos[o]=max(pos[ls[o]],pos[rs[o]]);
		else pos[o]=min(pos[ls[o]],pos[rs[o]]);
	}
	void insert(int pre,int &o,int l,int r,int p,int v){
		o=++tot;ls[o]=ls[pre];rs[o]=rs[pre];pos[o]=type?0:inf;
		if(l==r){pos[o]=v;return;}
		int mid=l+r>>1;
		if(p<=mid) insert(ls[pre],ls[o],l,mid,p,v);
		else insert(rs[pre],rs[o],mid+1,r,p,v);
		pushup(o);
	}
	int query(int o,int l,int r,int L,int R){
		if(!o) return type?-1:n+1;
		if(L<=l&&r<=R) return pos[o];
		int mid=l+r>>1;
		if(R<=mid) return query(ls[o],l,mid,L,R);
		if(L>mid) return query(rs[o],mid+1,r,L,R);
		if(type) return max(query(ls[o],l,mid,L,R),query(rs[o],mid+1,r,L,R));
		else return min(query(ls[o],l,mid,L,R),query(rs[o],mid+1,r,L,R));
	}
	int lsv[N],Cnt;
	int Bef(int p,int id){
		int p1=query(root[p-1],1,Cnt,id,Cnt);
		if(p1>1){
			int p2=query(root[p1-1],1,Cnt,id,Cnt);
			if(~p2)befmax[p]=p2+1;
			else befmax[p]=1;
		}else{
			befmax[p]=1;
		}
	}
	int Aft(int p,int id){
		int p1=query(root[p+1],1,Cnt,id,Cnt);
		if(p1<=n){
			int p2=query(root[p1+1],1,Cnt,id,Cnt);
			if(p2<=n)aftmax[p]=p2-1;
			else aftmax[p]=n;
		}else{
			aftmax[p]=n;
		}		
	}	int P[N];
	void init(){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&val[i]);lsv[i]=val[i];
			if(val[i]>MAXV)MAXV=val[i];
		}	for(lg=1;(1ll<<lg)<=MAXV;++lg);
		bin[0]=1;for(int i=1;i<=lg;i++)bin[i]=bin[i-1]<<1;
		sort(lsv+1,lsv+n+1);Cnt=unique(lsv+1,lsv+n+1)-lsv-1;
		type=1;for(int i=1;i<=n;i++){
			P[i]=lower_bound(lsv+1,lsv+Cnt+1,val[i])-lsv;
			Bef(i,P[i]);
			insert(root[i-1],root[i],1,Cnt,P[i],i);
		}	clear();
		type=0;pos[0]=inf;for(int i=n;i>=1;i--){
			Aft(i,P[i]);
			insert(root[i+1],root[i],1,Cnt,P[i],i);
		}
	}
}
int main(){
	Pre::init();int ans=0,now;
	for(int i=1;i<=n;i++)
		build(root[i-1],root[i],val[i]);
	for(int i=1;i<=n;i++){
		if(val[i]==MAXV) continue;
		now=query(val[i],befmax[i]-1,aftmax[i]);
		if(now>ans)ans=now;
	}	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VictoryCzt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值