bzoj-4551 [Tjoi2016&Heoi2016]树

题意:

给出一棵树,有两种操作:

C x:标记点x;

Q x:查询某个点的最近被标记祖先;

n,m<=100000;


题解:

首先我们发现如果标记了一个点,其影响是对于个子树,也就是一段DFS区间的;

那么我们可以转化成一个序列上的问题:区间加入一个值,单点查询最大值;

然后直接标记永久化搞个线段树套set就可以了,时间复杂度O(nlog^2n);

【我怎么突然感觉不用套set直接维护最小值就行呢。。。


代码:


#include<set>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define pr pair<int,int>
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
using namespace std;
typedef long long ll;
char op[N];
int next[N<<1],to[N<<1],head[N],ce;
int L[N],R[N],deep[N],tim;
bool cov[N];
set<pr>tr[N<<2];
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=head[x];
	head[x]=ce;
}
void dfs(int x,int pre)
{
	L[x]=++tim;
	deep[x]=deep[pre]+1;
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=pre)
		{
			dfs(to[i],x);
		}
	}
	R[x]=tim;
}
pr getmax(const set<pr> &s)
{
	if(!s.size())	return pr(0,0);
	return *(--s.end());
}
void update(int l,int r,int no,int st,int en,pr val)
{
	if(st<=l&&r<=en)
	{
		tr[no].insert(val);
	}
	else
	{
		int mid=l+r>>1;
		if(en<=mid)		update(lson,st,en,val);
		else if(st>mid)	update(rson,st,en,val);
		else	update(lson,st,en,val),update(rson,st,en,val);
	}
}
pr query(int l,int r,int no,int k)
{
	if(l==r)
		return getmax(tr[no]);
	else
	{
		int mid=l+r>>1;
		if(k<=mid)	return max(query(lson,k),getmax(tr[no]));
		else		return max(query(rson,k),getmax(tr[no]));
	}
}
int main()
{
	int n,m,i,j,k,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	dfs(1,0);
	cov[1]=1;
	tr[1].insert(pr(1,1));
	for(i=1;i<=m;i++)
	{
		scanf("%s%d",op,&x);
		if(op[0]=='C')
		{
			if(!cov[x])
			{
				update(1,n,1,L[x],R[x],pr(deep[x],x));
				cov[x]=1;
			}
		}
		else
		{
			printf("%d\n",query(1,n,1,L[x]).second);
		}
	}
	return 0;
}




你可能感兴趣的:(线段树,树,bzoj,dfs序)