HDU 6703 array 权值线段树

先不考虑修改的情况,对所有的权值建一颗线段树,然后线段树上存储下标的信息,然后每个结点维护子树下标的最大值。然后对于每次查询,我直接查询区间 [ k , n + 1 ] [k,n+1] [k,n+1]这段区间值大于r的最小的那个点。
对于修改的情形,实际上每个节点被修改之后相当于删除,也就是说该节点是可选的,把该节点的下标置为无穷大就可以了。
对于每次查询,先考虑查询左子树,如果左子树查询不到答案,再查询右子树,可以证明这个复杂度是 O ( l o g ) O(log) O(log)的。
对于查询的区间全部落在左子树和右子树的情形,我们归纳到子树中。剩下的情况只剩下查询区间横跨mid这个中点,对于这种情形,答案不存在左子树中则必定在右子树中,而右子树不会出现无解的情形。

#include
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e5+7;
int a[N];
int mx[N<<2];
void pushup(int rt) {
	mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
void build(int rt,int l,int r) {
	if(l==r) {
		mx[rt]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	pushup(rt);
}
void modify(int rt,int l,int r,int x,int v) {
	if(l==r) {
		mx[rt]=v;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify(rt<<1,l,mid,x,v);
	else modify(rt<<1|1,mid+1,r,x,v);
	pushup(rt);
}
int query(int rt,int l,int r,int L,int R,int v) {
	if(l==r&&L<=l&&r<=R) {
		if(mx[rt]>v) return l;
		else return -1;
	}
	if(mx[rt]<=v) return -1;
	int mid=(l+r)>>1;
	int ans=-1;
	if(l<=R&&L<=mid&&mx[rt<<1]>v) 
		ans=query(rt<<1,l,mid,L,R,v);
	if(ans!=-1) return ans;
	if(mid+1<=R&&L<=r&&mx[rt<<1|1]>v) 
		ans=query(rt<<1|1,mid+1,r,L,R,v);
	if(ans!=-1) return ans;
	return ans;
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		int n,q;
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		build(1,1,n+1);
		for(int i=1;i<=n;i++) 
			modify(1,1,n+1,a[i],i);
		modify(1,1,n+1,n+1,10000000);
		int ans=0;
		while(q--) {
			int opt,t1,t2,t3;
			scanf("%d",&opt);
			if(opt==1) {
				scanf("%d",&t1);
				t1=t1^ans;
				modify(1,1,n+1,a[t1],10000000);
			}
			else {
				scanf("%d%d",&t2,&t3);
				int r=t2^ans;
				int k=t3^ans;
				ans=query(1,1,n+1,k,n+1,r);
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}

你可能感兴趣的:(比赛题解)