【 2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛】1002.array【主席树】

题意:

给定一个长度为 n n 的序列,序列中每个数都不相同,数字范围在 1   n 1~n 之间,一共有 m m 次操作。操作 1 1 ( 1 , p o s ) (1,pos) ,令 a p o s = a p o s + 1 0 7 a_{pos}=a_{pos}+10^7 。操作 2 2 ( 2 , r , k ) (2,r,k) ,求出大于等于 k k 且不等于 a 1   a r a_1~a_r 中任意一个数的最小值,强制在线。 ( 1 n 1 0 5 , 1 m 1 0 5 ) (1\leq n\leq 10^5,1\leq m\leq 10^5)


思路:

首先,我们先考虑如果没有修改操作,这道题应该如何考虑。

假如没有修改操作,我们就直接建一颗主席树,然后 a 1 a n a_1~a_n 的数依次插入主席树中,作为标记节点,然后查询 1 r 1~r 中第一个大于等于 k k 的并且没有被标记的数。这个部分在线段树中的具体操作不难思考,详见代码。

然后我们考虑修改,一个数修改之后就表示这个数不再被标记。因此我们将所有被修改的数放入 s e t set 中,然后最终答案一定由 s e t set 中的值或者主席树中查询到的值取 m i n min 得到。

至此,此题结束。


总结:

反省一下,为什么比赛的时候没有做出来。

比赛时,一直思考的是如果直接从主席树中查到答案,因此写了一发带修改的主席树,然后成功 TLE \text{TLE} 。一直想着如何直接做出这道题,不思考如何转换,宛如 z z zz

而正确的思考方式应该是思考最终答案由哪些数组成,一个数被修改之后就意味着这个数重新变成了一个可以成为最终答案的数字。因此两者取 m i n min 即可得到答案。


代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int inf = 1e8;
const int N = 1e5+100;
const db EPS = 1e-9;
using namespace std;

int n,m,a[N],sz,root[N],sum[N*20],ls[N*20],rs[N*20];
set<int> st;

void build(int &now,int l,int r){
	if(!now) now = ++sz;
	sum[now] = 0;
	if(l == r) return;
	int mid = (l+r)>>1;
	build(ls[now],l,mid);
	build(rs[now],mid+1,r);
}

void insert(int pre,int &now,int l,int r,int pos,int c){
	if(!now) now = ++sz;
	if(l == r) {sum[now] += c; return;}
	int mid = (l+r)>>1;
	if(pos <= mid) {rs[now] = rs[pre]; insert(ls[pre],ls[now],l,mid,pos,c);}
	else {ls[now] = ls[pre]; insert(rs[pre],rs[now],mid+1,r,pos,c);}
	sum[now] = sum[ls[now]]+sum[rs[now]];
}

int query(int now,int l,int r,int k){
	if(sum[now] == r-l+1) return inf;
	if(l == r){
		if(!sum[now]) return l;
		else return inf;
	}
	int mid = (l+r)>>1;
	if(mid < k || sum[ls[now]] >= mid-l+1) return query(rs[now],mid+1,r,k);
	else{
		int ans = query(ls[now],l,mid,k);
		if(ans != inf) return ans;
		else return query(rs[now],mid+1,r,k);
	}
}

int main()
{
	int _; scanf("%d",&_);
	while(_--){
		sz = 0; st.clear();
		scanf("%d%d",&n,&m);
		rep(i,1,n) scanf("%d",&a[i]);
		build(root[0],1,n+1);
		rep(i,1,n) insert(root[i-1],root[i],1,n+1,a[i],1);
		int last_ans = 0;
		rep(i,1,m){
			int op; scanf("%d",&op);
			if(op == 1){
				int pos; scanf("%d",&pos);
				pos ^= last_ans;
				if(a[pos] == inf) continue;
				else{
					st.insert(a[pos]);
					a[pos] = inf;
				}
			}
			else{
				int r,k; scanf("%d%d",&r,&k);
				r ^= last_ans; k ^= last_ans;
				int hp1 = query(root[r],1,n+1,k), hp2 = inf;
				if(st.size()){
					set<int>::iterator it = st.lower_bound(k);
					if(it != st.end()) hp2 = (*it);
				}
				printf("%d\n",min(hp1,hp2));
				last_ans = min(hp1,hp2);
			}
		}
		rep(i,0,sz) ls[i] = rs[i] = sum[i] = 0;
		rep(i,0,n) root[i] = 0;
	}
	return 0;
}
发布了244 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/100045217