HDU6703

2019CCPC网络选拨赛1002

这 里 要 用 到 权 值 线 段 树 , 因 为 题 目 保 证 了 a i 属 于 [ 1 , n ] , 且 不 重 复 。 这里要用到权值线段树,因为题目保证了a_i属于[1,n],且不重复。 线ai[1,n] 那 么 用 权 值 为 下 标 , 线 段 树 的 值 是 i , 即 原 序 列 的 下 标 。 那么用权值为下标,线段树的值是i,即原序列的下标。 线i,
题 目 可 以 转 化 为 求 解 [ k , n ] 中 , 第 一 个 线 段 树 的 值 大 于 r 的 下 标 是 多 少 题目可以转化为求解[k,n]中,第一个线段树的值大于r的下标是多少 [k,n]线r
不 存 在 的 情 况 输 出 n + 1 就 行 了 , 当 然 如 果 一 开 始 k > n 了 , 不存在的情况输出n+1就行了,当然如果一开始k>n了, n+1k>n 直 接 输 出 k 即 可 直接输出k即可 k
首 先 建 立 一 个 维 护 最 大 值 可 以 单 点 修 改 的 线 段 树 , 区 间 查 询 怎 么 写 首先建立一个维护最大值可以单点修改的线段树,区间查询怎么写 线
就 是 这 道 题 目 的 精 髓 所 在 了 。 就是这道题目的精髓所在了。
显 然 , 最 左 边 符 合 条 件 的 点 就 是 答 案 。 显然,最左边符合条件的点就是答案。
优 先 遍 历 左 子 树 , 若 左 子 树 中 存 在 答 案 , 结 束 查 询 ; 若 不 存 在 , 则 优先遍历左子树,若左子树中存在答案,结束查询;若不存在,则
查 询 右 子 树 。 直 接 这 样 做 , 单 次 查 询 其 实 是 O ( n ) 的 , 需 要 进 行 减 枝 。 查询右子树。直接这样做,单次查询其实是O(n)的,需要进行减枝。 O(n)
每 次 查 询 一 颗 子 树 前 先 取 其 最 大 值 与 r 比 较 , 小 于 等 于 r 就 可 以 跳 过 每次查询一颗子树前先取其最大值与r比较,小于等于r就可以跳过 rr
这 个 子 树 了 , 而 最 大 值 在 单 点 修 改 时 是 维 护 好 的 , 现 在 的 调 用 是 O ( 1 ) 这个子树了,而最大值在单点修改时是维护好的,现在的调用是O(1) O(1)
的 , 所 以 整 个 查 询 的 复 杂 度 是 O ( l o g n ) 的 。 的,所以整个查询的复杂度是O(logn)的。 O(logn)
至 于 单 点 修 改 , 就 是 将 那 个 权 值 的 节 点 修 改 为 任 意 大 于 1 0 5 的 值 即 可 。 至于单点修改,就是将那个权值的节点修改为任意大于10^5的值即可。 105

#include<bits\stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5+10;
const int inf = 1e9+7; 
int T;
int n,m;
ll ans;
int arr[maxn],a[maxn];
struct TREE{
	int id;
	int v;
}tree[(maxn<<2)+10];
void build(int l,int r,int index){
	if(l==r){
		tree[index].id=a[l];
		tree[index].v=l;
	}
	else{
		int mid=l+((r-l)>>1);
		build(l,mid,index<<1);
		build(mid+1,r,(index<<1)|1);
		if(tree[index<<1].id>tree[(index<<1)|1].id){
			tree[index].id=tree[index<<1].id;
			tree[index].v=tree[index<<1].v;
		}
		else{
			tree[index].id=tree[(index<<1)|1].id;
			tree[index].v=tree[(index<<1)|1].v;			
		}
	}
}
void update(int dis,int l,int r,int index,int val){
	if(l==r){
		tree[dis].id=val;
		return ;
	}
	int mid=l+((r-l)>>1);
	if(index<=mid)
		update(dis<<1,l,mid,index,val);
	else
		update((dis<<1)|1,mid+1,r,index,val);
	if(tree[dis<<1].id>tree[(dis<<1)|1].id){
		tree[dis].id=tree[dis<<1].id;
		tree[dis].v=tree[dis<<1].v;
	}
	else{
		tree[dis].id=tree[(dis<<1)|1].id;
		tree[dis].v=tree[(dis<<1)|1].v;			
	}
}

int query(int dis,int l,int r,int ql,int qr,int val){
	if(ql>r||qr<l)return -1;
	if(l==r){
		if(tree[dis].id>val)
			return tree[dis].v;
		else return -1;
	}
	int res,mid=l+((r-l)>>1);
	if(tree[dis<<1].id>val)
		res=query(dis<<1,l,mid,ql,qr,val);
	else
		res=-1;
	if(res!=-1)return res;
	else{
		if(tree[(dis<<1)|1].id>val)
			res=query((dis<<1)|1,mid+1,r,ql,qr,val);
		else return -1;
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		ans=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&arr[i]);
			a[arr[i]]=i;
		}
		build(1,n,1);
		int flag;
		int t1,r,k;
		for(int i=1;i<=m;i++){
			scanf("%d",&flag);
			if(flag==1){
				scanf("%d",&t1);
				t1=t1^ans;
				update(1,1,n,arr[t1],inf);
			}
			else{
				scanf("%d%d",&r,&k);
				r=r^ans;k=k^ans;
				if(k>n)ans=k;
				else {
					int res=query(1,1,n,k,n,r);
					if(res==-1)ans=n+1;
					else ans=res;
				}
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值