【HDU 6703】array

题目链接。题意有点绕,但是仔细读一下还是可以读出来的。给一个数组a,数组元素是n的排列,长度1e5,然后两种操作.

1.(1,pos)。把a[pos]给变成a[pos]+1e7

2.(2,r,k)。询问大于等于k的一个数,并且这个数不能等于a[1,r]区间内的任意一个数。1<=k<=n。

询问1e5次。

额,上来看觉得主席树轻松秒,然后查询树上二分。其实就是再权值数组上操作。因为主席树天生自带一个log再加上树上二分一个log,nlogn^2.TLE.仔细思考了一下,发现没必要主席树。

分析:

首先,还是用数组b[i]=j,代表数组中的i的当前位置是j。

      (1)如果是操作1,那么这个数就没用了,因为它被加了一个很大的数上去,已经不再这个数组里。那么可以给他一个INF,其实没必要INF,n+1就够了,下面说为什么n+1就行。

       (2)对于每个查询,答案最大是n+1,因为r<=n,所以在[1,r]中的元素不会超过n,那么最大的不等于[1,n]中的所有元素的值就是n+1.所以当一个数被修改后,可以直接把b数组里这个数对应的值改为n+1.即可。那么怎么通过b数组找答案呢?对于每个查询,其实我们需要的是[x,n]这个区间内,第一个出现的数字,并且这个数字在b数组对应的值要大于r。解释一下就是,在[x,n]这个区间内,从x一直向右走,遇到一个元素,检查它的在b数组里的值,在b数组里的值其实也就是他在元素组中的下标,如果这个下标<r,说明这个值在[1,r]之间,不是我们想要的,继续向前走。分析到这里,其实做法就显然了,对b数组建立线段树,节点维护当前区间所有数下标的最大值,这棵树也是一颗权值线段树。查询贪心的查左子树,如果左子树可行,就往左子树跑,检查的规则就是看一下当前子树节点的存的哪个最大值是不是大于r,如果不是,那么说明这段区间是不可行的,否则可以递归下去找到产生最大值得那个点,然后因为可能会找到几个满足条件点,所以在儿子返回结果时,也就是回溯回来,在这些结果里取一个最小值作为答案。本题结束。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, m;
int a[MAXN], b[MAXN];
int pos[MAXN << 2];
void pushup(int rt) {
	pos[rt] = max(pos[rt << 1], pos[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
	if (l == r) {
		pos[rt] = b[l];
		return;
	}

	int mid = l + r >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	pushup(rt);
}
void update(int k, int l, int r, int rt) {
	if (l == r) {
		pos[rt] = n + 1;
		return;
	}

	int mid = l + r >> 1;
	if (k <= mid) update(k, l, mid, rt << 1);
	else update(k, mid + 1, r, rt << 1 | 1);
	pushup(rt);
}
int query(int ql, int qr, int k, int l, int r, int rt) {
	int mid = l + r >> 1;
	if (ql <= l && qr >= r) {
		if (l == r) {
			if (pos[rt] > k) return l;
			else return n + 1;
		}

		if (pos[rt << 1] > k) return query(ql, qr, k, l, mid, rt << 1);
		if (pos[rt << 1 | 1] > k) return query(ql, qr, k, mid + 1, r, rt << 1 | 1);
		return n + 1;
	}

	if (qr <= mid) return query(ql, qr, k, l, mid, rt << 1);
	if (ql > mid) return query(ql, qr, k, mid + 1, r, rt << 1 | 1);
	return min(query(ql, qr, k, l, mid, rt << 1), query(ql, qr, k, mid + 1, r, rt << 1 | 1));
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int las = 0;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[a[i]] = i;
		build(1, n, 1);
		for (int i = 1; i <= m; i++) 
		{
			int opt, x, y;
			scanf("%d%d", &opt, &x); x ^= las;
			if (opt == 1) update(a[x], 1, n, 1);
			else 
			{
				scanf("%d", &y); y ^= las;
				int curans = query(y, n, x, 1, n, 1);
				printf("%d\n", curans);
				las=curans;
			}
		}
	}
	return 0;
}
发布了370 篇原创文章 · 获赞 48 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/102650130