2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛HDU6703(权值线段树)

题目链接:array
题意:
  给你一个数组a[],大小为n,范围也在1~n。两个操作:

  1. (1,pos),indicating to change the value of apos to apos+10,000,000;
  2. (2,r,k),indicating to ask the minimum value which is not equal to any ai ( 1≤i≤r ) and **not less ** than k.

1 将a[pos]的值加100000000
2 询问大于等于k且不等于a[i]的最小的一个数是多少,i从1~r

此题要求强制在线:

For instructions in format 1 , we defined pos=t1⊕LastAns . (It is promised that 1≤pos≤n)
For instructions in format 2 , we defined r=t2⊕LastAns,k=t3⊕LastAns. (It is promised that 1≤r≤n,1≤k≤n )

也就是每次询问或者修改都要异或上次的结果。

思路:
题目保证每次询问或者修改都是合法的。且 1 < = k < = n 1<=k<=n
对于修改操作,将a[pos]加上一个很大的数,可以预见之后这个位置上的数就没有用了,因为询问k和增加的数相差实在太大了。
那么我们可以建立一颗权值线段树,范围是1~n+1。每个区间记录这个区间里标的最大值(优化查询)。
范围为什么是1~n+1?
如果我们询问的是k是n,那么答案应该就是n+1或者n。所以n+1有可能成为答案。
如何查询?
如果我们的查询是2 r k。那么首先查询区间应该是[k,n+1],其次为了满足不等于a[i]这个条件,那么我们的下标还应该大于r(小于等于的话就和前面的相同了)。那在满足这两个条件的前提下。这个位置应该尽量靠近k。也就是每次查询的时候优先查询左子树。
如何优化查询?
我们记录了每个区间的下标的最大值,这时候就可以派上用场了。每次查询前都先判断要查询的区间的下标最大值是否大于r,如果最大值小于等于r,那么就可以不用查询了。 此外如果左子树不能查询或者查询不到答案就直接查询右子树(因为题目保证又解)。
修改
当要修改一个数的时候,直接把他的下标改为无穷大就好。这样以后查询的时候这个位置的值总是可以被考虑到的(必定大于r)
官方题解

因为数组中的值唯一,且在1到n的范围内,而询问的r和k也在1到n的范围内。 所以对于任意一个被操作1修改过的值都不会成为询问的答案,而询问的结果也必然在k到n+1的范围内。 因为没有被修改过值是唯一的,所以可以建立权值线段树,维护权值区间内的值所在下标的最大值。而询问则转化为不小于k的值里面,下标超过r的最小权值是多少。 如何处理询问呢,一种较为暴力的解法是直接在线段树上询问权值在k到n+1的范围内第一个下标超过r的权值是多少。但复杂度可能会被卡,需要减枝。 再加上一个额外的判断就可以了,就是在递归查询完左子树内存不存在大于r的下标之后,如果不存在,则先看一下右子树内的下标的最大值是否大于r。如果不大于r,则不必再进入右子树内查询,否则答案一定在右子树内。在进左子树之前也利用同样的判断条件来判断是否有必要进入左子树,这样做可以保证单次查询的复杂度是O(log n) 的。 而对于操作1,则等价于修改某个权值的下标为无穷大。操作复杂度也是O(log n )的。 综上所述,得到了一个复杂度为O( m * log n )的在线算法,可以较快地通过此

#include<bits/stdc++.h>
using namespace std;
const int N = 100 * 1000 + 10;
int n,m,a[N],to[N];
int sum[N << 2];

void down(int rt) {
	sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);
}
void built(int rt, int l, int r) {
	if (l == r) {
		sum[rt] = to[l];
		return;
	}
	int mid = l + r >> 1;
	built(rt << 1, l, mid);
	built(rt << 1|1, mid+1,r);
	down(rt);
}

void change(int rt, int l, int r, int pos) {
	if (l == r) {
		sum[rt] = n+10;return;
	}
	int mid = l + r >> 1;
	if (pos <= mid)change(rt<<1,l,mid,pos);
	else change(rt << 1 | 1, mid + 1, r,pos);
	down(rt);
}

int query(int rt,int l,int r,int pos,int k) {
	if (l == r) {
		if (sum[rt] > k)return l;
		else return n + 10;
	}
	int mid = l + r >> 1;
	int ans=n+10;
	if (pos <= mid) {
		if (sum[rt << 1] > k)ans=query(rt << 1, l, mid, pos, k);
	}
	if (ans != n + 10)return ans;
	return query(rt << 1 | 1, mid + 1, r, pos, k);
}

int main() {
	freopen("a.in","r",stdin);

	int t; scanf("%d",&t);
	while (t--) {
		scanf("%d%d",&n,&m);
		for (int i = 1; i <= n; i++)scanf("%d",a+i),to[a[i]]=i;
		to[n + 1] = n + 1; built(1, 1, n + 1);
		int ans = 0;
		while (m--) {
			int op, t1, t2;
			scanf("%d",&op);
			if (op == 1) {
				scanf("%d",&t1);
				change(1,1,n+1,a[t1^ans]);
			}
			else {
				scanf("%d%d",&t1,&t2);
				printf("%d\n",ans=query(1,1,1+n,t2^ans,t1^ans));
			}
		}
	}

	return 0;
}
发布了70 篇原创文章 · 获赞 5 · 访问量 7167

猜你喜欢

转载自blog.csdn.net/xiaonanxinyi/article/details/100054065