array HDU - 6703(主席树+set)

题目链接

题意:略

思路:对于操作1,a[i]修改后原a[i]的值可以成为以后询问时的答案,压入set中;操作2在区间[r+1,n]上找大于等于K的值,因为可能是修改操作的值,从set中找到大于等于k的值与主席树中区间[r+1,n]中大于等于k的值取小的即可;利用set避免在主席树上做修改,即使主席树上查到的值非法(值被修改了)在set中仍会找到合法值,并不冲突。

代码:

#include
#include
#include
#include
using namespace std;
const int maxn=1e5+1000;
struct node
{
	int l,r,num;
}tree[maxn*40];
int n;
int root[maxn],tot;
int a[maxn];
int sz=maxn;
void update(int l,int r,int &x,int y,int pos)
{
	tree[++tot]=tree[y];
	x=tot;
	tree[x].num++;
	if(l==r) return ;
	int m=(l+r)>>1;
	if(pos<=m) update(l,m,tree[x].l,tree[y].l,pos);
	else update(m+1,r,tree[x].r,tree[y].r,pos); 
}
int query(int l,int r,int x,int y,int L,int R)
{
	if(l==r){
		if(tree[y].num-tree[x].num>0)//该点是否有值 
			return l;
		else return n+1;//n+1一定合法 
	}
	int m=(l+r)>>1;
	int tmp=tree[tree[y].l].num-tree[tree[x].l].num;
	int ans=n+1;
	if(L<=m&&tmp>0) ans=min(ans,query(l,m,tree[x].l,tree[y].l,L,R));//tmp>0 左区间可能有解 
	if(R>m&&ans==n+1) ans=min(ans,query(m+1,r,tree[x].r,tree[y].r,L,R));//左区间无解找右区间 
	return ans;
}
int main()
{
	int kase;
	scanf("%d",&kase);
	while(kase--)
	{
		set s;
		tot=0;
		int q;
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) update(1,sz,root[i],root[i-1],a[i]);
		int lastans=0;
		while(q--)
		{
			int op;
			scanf("%d",&op);
			if(op==1){
				int pos;
				scanf("%d",&pos);
				pos^=lastans;
				s.insert(a[pos]);
			}else{
				int r,k;
				scanf("%d%d",&r,&k);
				r^=lastans;k^=lastans;
				int ans=1e9;
				if(s.lower_bound(k)!=s.end()){
					ans=*s.lower_bound(k);
				}
				ans=min(ans,query(1,sz,root[r],root[n],k,n));
				printf("%d\n",ans);
				lastans=ans;
			}
		}
	} 
	return 0;
 } 

 

你可能感兴趣的:(算法,主席树)