[SPOJ QTREE3]Query on a tree again!

题目链接:https://www.spoj.com/problems/QTREE3/
大意
给一棵有 n n n个节点的树,树上每个节点可以是黑色或白色,起初所有节点都是白色,有Q个操作,操作的输入格式为
a a a b b b

  • a = 0 a=0 a=0时,代表改变节点 b b b的颜色
  • a = 1 a=1 a=1时,代表要求输出从节点 1 1 1到节点 b b b的路径上第一个黑色的点的编号,若不存在黑色的点则输出-1。

思路
树链剖分,只不过需要维护的内容不是点权/边权而是最靠近节点 1 1 1的满足条件的节点编号。

  • 细节:因为是寻找离节点 1 1 1最近的节点,所以可以以 1 1 1为根建树,查找时优先查找线段树上距离节点 1 1 1较近的一边

ac代码

#include<stdio.h>
#include<algorithm>
#define LL long long
#define N 150005
using namespace std;
int n,a,b,cot;
int p[N*2],head[N],nxt[N*2],q,val[N];
int f[N],depth[N],top[N],son[N],size[N],pos[N],cnt,A[N];
char s[20];
struct st{
	LL l,r,color,min_num,exist;
};//线段树结构体 
struct st dat[800020];
LL c[200020];//线段树的结构数组
void build(LL now,LL l,LL r);
void update(LL now,LL l,LL r,LL s);
inline void initialize_1(LL now,LL l); 
inline void initialize_2(LL now);
inline void refresh_1(LL now,LL s);
inline void refresh_2(LL now);
void dfs1(int u,int v)
{
	f[u]=v;
	size[u]=1;son[u]=0;
	depth[u]=depth[v]+1;
	for(int i=head[u];i;i=nxt[i])
	{
		if(p[i]!=v)
		{
			dfs1(p[i],u);
			size[u]+=size[p[i]];
			if(size[p[i]]>size[son[u]])son[u]=p[i];
		}
	}
}
void dfs2(int u,int v,int to_p)
{
	top[u]=to_p;
	pos[u]=++cnt;
	A[cnt]=val[u];
	if(son[u]) dfs2(son[u],u,to_p);
	for(int i=head[u];i;i=nxt[i])
	{
		if(p[i]!=v&&son[u]!=p[i]) dfs2(p[i],u,p[i]);
	}
}
int qu(int now,int l,int r)
{
	if(dat[now].l==l&&dat[now].r==r)
	{
		return dat[now].min_num;
	}
	int mid=(dat[now].l+dat[now].r)>>1;
	if(l>mid)return qu(now*2+1,l,r);
	else if(r<=mid)return qu(now*2,l,r);
	else 
	{
		int t1=qu(now*2,l,mid);
		int t2=qu(now*2+1,mid+1,r);
		if(t1!=0x7fffff)return t1;else return t2;
	}
}
int query(int target)
{
	int ans=0x7fffff;
	while(top[target]!=1)
	{
		int t=qu(1,pos[top[target]],pos[target]);
		if(t!=0x7fffff)
		if(ans==0x7fffff||depth[ans]>depth[t])ans=t;
		target=f[top[target]];
	}
	int t=qu(1,pos[top[target]],pos[target]);
	if(t!=0x7fffff)
	if(ans==0x7fffff||depth[ans]>depth[t])ans=t;
	if(ans==0x7fffff)return -1;else return ans;
}
int main()
{
	scanf("%d",&n);	scanf("%d",&q);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&a,&b);
		p[++cot]=a;nxt[cot]=head[b];head[b]=cot;
		p[++cot]=b;nxt[cot]=head[a];head[a]=cot;
	}
	dfs1(1,0);//建树,划分层次,找重儿子 
	dfs2(1,0,1);//分轻链重链,映射每条链至线段树 
	build(1,1,n);
	while(q--)
	{
		scanf("%d%d",&a,&b);
		if(!a) 
		{
			update(1,pos[b],pos[b],b);
		}
		else printf("%d\n",query(b));
	}
}
void build(LL now,LL l,LL r)
{
	LL mid=(l+r)>>1;
	dat[now].l=l;dat[now].r=r;
	if(l==r)
	{
		initialize_1(now,l);
		return;
	}else
	{
		build(now*2,l,mid);
		build(now*2+1,mid+1,r);
		initialize_2(now);
		return;
	}
}

void update(LL now,LL l,LL r,LL s)
{
	if(l==dat[now].l&&r==dat[now].r)
	{
		refresh_1(now,s);
		return;
	}
	LL mid=(dat[now].l+dat[now].r)>>1;
	if(l>mid){update(now*2+1,l,r,s);}else
	if(r<=mid){update(now*2,l,r,s);}else
	{
		update(now*2,l,mid,s);
		update(now*2+1,mid+1,r,s);
	}
	refresh_2(now);
	return;
}
inline void initialize_1(LL now,LL l)
{
	dat[now].color=0;dat[now].exist=0;dat[now].min_num=0x7fffff;
}
inline void initialize_2(LL now)
{
	dat[now].min_num=min(dat[now*2].min_num,dat[now*2+1].min_num);
	dat[now].exist=dat[now].min_num!=0x7fffff;
} 
inline void refresh_1(LL now,LL s)
{
	dat[now].color^=1;dat[now].exist=dat[now].color;
	if(dat[now].exist) dat[now].min_num=s;else dat[now].min_num=0x7fffff;
}
inline void refresh_2(LL now)
{
	if(dat[now*2].min_num!=0x7fffff)dat[now].min_num=dat[now*2].min_num;
	else dat[now].min_num=dat[now*2+1].min_num;
	dat[now].exist=dat[now].min_num!=0x7fffff;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值