【2019年CCPC网络赛】【HDU 6703】【array】【线段树】

题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=6703
题意:
给一个数组 a n a_n an a i ∈ [ 1 , n ] a_i∈[1,n] ai[1,n],且各不相同
现在有两种操作:
①将 a p o s a_{pos} apos加上 1 0 7 10^7 107
②查找满足大于等于 k k k且不等于 a 1 , a 2 , ⋅ ⋅ ⋅ , a r a_1,a_2,···,a_r a1,a2,,ar中的任意一个的最小值
这里得到的操作都要异或上次的答案
另外题目保证每次得到的 p o s , r , k ∈ [ 1 , n ] pos, r, k∈[1,n] pos,r,k[1,n]
题解:
显然是一道强制在线的题,要按顺序处理
需要注意的是,答案可以不是当前数列中的数,比如将 1 − > 1 0 7 + 1 1->10^7+1 1>107+1,然后查找大于1的且不等于 a 1 , a 2 , ⋅ ⋅ ⋅ , a n a_1,a_2,···,a_n a1,a2,,an的最小值,这里答案可以是1,并且可以得出 答案是在 [ 1 , n + 1 ] [1,n+1] [1,n+1] 的结论
由于每次得到的 p o s , r , k ∈ [ 1 , n ] pos, r, k∈[1,n] pos,r,k[1,n]因而每次操作后都不会变成答案,而且不管怎么操作,每个数都会是不一样的
这里有修改和区间内查询操作,显然是一道线段树题(比赛的时候想歪了,往动态主席树啥的想,一顿操作猛如虎,回首一看线段树,笨比至极)
先问题转化为:
求值在 [ k , n + 1 ] [k,n+1] [k,n+1]内并且坐标大于 r r r的最小值
我们用线段树节点本身代表的就是数值,每个点都记录当前区间 [ l , r ] [l,r] [l,r]内坐标的最大值 p p p如果区间的最大坐标都小于 r r r 就没必要找了
所以在查找的时候,先找到 [ k , n + 1 ] [k,n+1] [k,n+1]的区间节点(下面的query),然后在这里面再找坐标大于 r r r的最小值(下面的find)
其中find函数需要一个剪枝,因为是找最小值,所以如果左子树找不到,才会找右子树,因为左子树数值更小
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define lc u<<1
#define rc u<<1|1
#define m (l+r)/2
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 2e5 + 10;


int T, N, M;
int a[MAX], pos[MAX];

struct SegTree {
	int l, r, p;
}t[MAX << 2];

int ans;

void push_up(int u) {
	t[u].p = max(t[lc].p, t[rc].p);
}

void build(int u, int l, int r) {
	t[u].l = l, t[u].r = r;
	if (l == r) {
		t[u].p = pos[l];//记录坐标
		return;
	}
	build(lc, l, m); build(rc, m + 1, r);
	push_up(u);
}

void update(int u, int P) {
	if (P < t[u].l || P > t[u].r)return;
	if (t[u].l == t[u].r) {
		t[u].p = N + 1;//将更新的节点都指向N+1
		return;
	}
	update(lc, P); update(rc, P);
	push_up(u);
}

void find(int u, int r) {//再找坐标大于r的最小值
	if (t[lc].p > r)find(lc, r);
	else if (t[rc].p > r)find(rc, r);//剪枝操作,左边找不到才去找右边
	if (t[u].p > r && t[u].l == t[u].r)ans = min(ans, t[u].l);
}

void query(int u, int l, int r, int R) {//先找到[k,n+1]
	if (t[u].l >= l && t[u].r <= r) {
		find(u, R);
		return;
	}
	int mid = (t[u].l + t[u].r) / 2;
	if (l <= mid)query(lc, l, r, R);
	if (r > mid)query(rc, l, r, R);
}

int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &N, &M);
		for (int i = 1; i <= N; i++) {
			scanf("%d", &a[i]);
			pos[a[i]] = i;
		}
		pos[N + 1] = N + 1;
		build(1, 1, N + 1);
		ans = 0;//最初的ans为0
		for (int i = 1; i <= M; i++) {
			int op;
			scanf("%d", &op);
			if (op == 1) {
				int n;
				scanf("%d", &n); n ^= ans;
				update(1, a[n]);
			}
			else {
				int k, r;
				scanf("%d%d", &r, &k); r ^= ans, k ^= ans;
				ans = INF;
				query(1, k, N + 1, r);
				printf("%d\n", ans);
			}
		}
	}
	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值