luogu P4116 Qtree3

背景:

同今天的上一篇。

题目传送门:

https://www.luogu.org/problemnew/show/P4116

题意:

一棵树,每一个点有两种颜色,现在支持更换点的颜色和查询 1 1 1 x x x路径上某种颜色的出现的第一次位置。

思路:

用树剖即可。
改颜色照常该。
询问位置只需在线段树中再次查询即可(注意要取 d e e p deep deep更小的)。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
	int n,m,q,u=0,len=0;
	struct node1{int x,y,next;} a[200010];
	struct node2{int d,l,r,lc,rc;} tr[200010];
	int last[200010],p[200010],tot[200010],son[200010],fa[200010],dep[200010],ys[200010],yss[200010],top[200010],ans[200010];
void ins(int x,int y)
{
	a[++len]=(node1){x,y,last[x]}; last[x]=len;
}
void build(int l,int r)
{
	int now=++len;
	tr[now]=(node2){0,l,r,-1,-1};
	if(l<r)
	{
		int mid=(l+r)>>1;
		tr[now].lc=len+1; build(l,mid);
		tr[now].rc=len+1; build(mid+1,r);
	}
}
void change(int now,int x)
{
	if(tr[now].l==tr[now].r)
	{
		tr[now].d^=1;
		return;
	}
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(x<=mid) change(lc,x); else change(rc,x);
	tr[now].d=tr[lc].d+tr[rc].d;
}
int findsum(int now,int l,int r)
{
	if(tr[now].l==l&&tr[now].r==r) return tr[now].d;
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(r<=mid) return findsum(lc,l,r);
	else if(mid+1<=l) return findsum(rc,l,r);
	else return findsum(lc,l,mid)+findsum(rc,mid+1,r);
}
int find(int now,int l,int r)
{
	if(l==r) return yss[l];
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(r<=mid) return find(lc,l,r);
	else if(mid+1<=l) return find(rc,l,r);
	else return findsum(lc,l,mid)?find(lc,l,mid):find(rc,mid+1,r);
}
void dfs1(int x)
{
	tot[x]=1;
	son[x]=0;
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y==fa[x]) continue;
		fa[y]=x;
		dep[y]=dep[x]+1;
		dfs1(y);
		if(tot[son[x]]<tot[y]) son[x]=y;
		tot[x]+=tot[y];
	}
}
void dfs2(int x,int tp)
{
	ys[x]=++u;
	yss[u]=x;
	top[x]=tp;
	if(son[x]) dfs2(son[x],tp);
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y!=son[x]&&y!=fa[x]) dfs2(y,y);
	}
}
int solve(int x,int y)
{
	int tx=top[x],ty=top[y],t1=0,t2=0;
	while(tx!=ty)
	{
		if(dep[tx]>dep[ty]) swap(x,y),swap(tx,ty);
		if(findsum(1,ys[ty],ys[y])) t1=ys[ty],t2=ys[y];
		y=fa[ty];ty=top[y];
	}
	if(dep[x]>dep[y]) swap(x,y);
	if(findsum(1,ys[x],ys[y])) t1=ys[x],t2=ys[y];
	if(t1&&t2) return find(1,t1,t2); else return -1;
}
int main()
{
	int t,x,y;
	scanf("%d %d",&n,&m);
	for(int i=1;i<n;i++)
	{
		scanf("%d %d",&x,&y);
		ins(x,y),ins(y,x);
	}
	len=0;
	build(1,n);
	fa[1]=0;
	dep[1]=1;
	dfs1(1);
	dfs2(1,0);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d",&t,&x);
		if(!t) change(1,ys[x]); else printf("%d\n",solve(1,x));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值