[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;
}
### 关于蓝桥杯 C/C++ 大学生 A 组竞赛的相关题解与资料 #### 1. **备赛经验** 对于蓝桥杯 C/C++ 大学生 A 组的比赛,参赛者需要具备扎实的基础知识以及丰富的实战经验。一位退役的 HEOI 和 CCPC 选手分享了他的备赛心得[^1]。他强调了以下几个方面的重要性: - 掌握核心知识点并能够熟练转化为代码实现。 - 注重算法和数据结构的学习,同时适当了解一些框架的应用场景。 #### 2. **常见问题分析** 在第十四届蓝桥杯比赛中,有参赛者提到自己虽然掌握了较多的知识点,但在实际编码过程中遇到了困难[^2]。这种现象表明理论学习与实践应用之间存在差距,因此建议通过大量练习来弥补这一不足之处。 #### 3. **具体题目解析** 以下是部分经典试题及其解答思路: ##### (1) 填空类问题 以某一年的一道填空题为例,可以通过暴力枚举的方式快速得出答案。例如,在给定范围内统计符合条件的整数数量时,可以采用如下方法完成计算[^3]: ```cpp #include <bits/stdc++.h> using namespace std; int main(){ int ans = 0; for(int i=1;i<=100000000;++i){ int cnt=0; for(int j=i;j;j/=10)cnt++; if(cnt&1) continue; int sum=0,now=0; for(int j=i;j;j/=10){ now++; if(now<=cnt/2) sum += j % 10; else sum -= j % 10; } if(!sum) ans++; } cout << ans << endl; } ``` ##### (2) 动态规划 / 搜索 类型 另一典型问题是关于动态规划或者搜索策略的选择。这类题目通常要求设计状态转移方程或构建合理的搜索空间。例如,“有奖问答”类型的题目可能涉及复杂的路径探索或多阶段决策优化。 ##### (3) 数据结构综合运用 某些复杂度较高的题目则考验选手对高级数据结构的理解程度。比如曾经有一道需要用到三维状数组进行区间修改操作的问题,尽管可以用暴力法解决,但从效率角度考虑显然不是最佳方案[^4]。 #### 4. **推荐资源** 为了更好地准备比赛,可以从以下几个方向入手收集材料: - 参考历年真题及官方发布的标准答案; - 加入相关论坛社区交流心得体会; - 利用在线平台刷题巩固所学概念; --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

VictoryCzt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值