洛谷P4116 Qtree3

题目描述

给出N个点的一棵树(N-1条边),节点有白有黑,初始全为白

有两种操作:

0 i : 改变某点的颜色(原来是黑的变白,原来是白的变黑)

1 v : 询问1到v的路径上的第一个黑点,若无,输出-1

输入输出格式

输入格式:

第一行 N,Q,表示N个点和Q个操作

第二行到第N行N-1条无向边

再之后Q行,每行一个操作"0 i" 或者"1 v" (1 ≤ i, v ≤ N).

输出格式:

对每个1 v操作输出结果

输入输出样例

输入样例#1:  复制
9 8
1 2
1 3
2 4
2 9
5 9
7 9
8 9
6 8
1 3
0 8
1 6
1 7
0 2
1 9
0 2
1 9 
输出样例#1:  复制
-1
8
-1
2
-1

说明

For 1/3 of the test cases, N=5000, Q=400000.

For 1/3 of the test cases, N=10000, Q=300000.

For 1/3 of the test cases, N=100000, Q=100000.







树链剖分+线段树。

线段树维护 树剖序号 最小值,黑色节点值为 树剖序号,白色节点为 最大值。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) b[x].data
#define LSIDE(x) b[x].l
#define RSIDE(x) b[x].r
#define MAXN 100010
#define MAX 2147483640
using namespace std;
int n,m,c=1,d=1;
int head[MAXN],deep[MAXN],son[MAXN],fa[MAXN],top[MAXN],size[MAXN],id[MAXN],nid[MAXN];
struct node1{
	int next,to;
}a[MAXN<<1];
struct node2{
	int data,l,r;
}b[MAXN<<2];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline void add(int x,int y){
	a[c].to=y;
	a[c].next=head[x];
	head[x]=c++;
	a[c].to=x;
	a[c].next=head[y];
	head[y]=c++;
}
void dfs1(int rt){
	son[rt]=0;size[rt]=1;
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(!deep[will]){
			deep[will]=deep[rt]+1;
			fa[will]=rt;
			dfs1(will);
			size[rt]+=size[will];
			if(size[son[rt]]<size[will])son[rt]=will;
		}
	}
}
void dfs2(int rt,int f){
	nid[d]=rt;id[rt]=d++;top[rt]=f;
	if(son[rt])dfs2(son[rt],f);
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(will!=fa[rt]&&will!=son[rt])
		dfs2(will,will);
	}
}
inline void pushup(int rt){
	DATA(rt)=min(DATA(LSON),DATA(RSON));
}
void buildtree(int l,int r,int rt){
	int mid;
	LSIDE(rt)=l;
	RSIDE(rt)=r;
	if(l==r){
		DATA(rt)=MAX;
		return;
	}
	mid=l+r>>1;
	buildtree(l,mid,LSON);
	buildtree(mid+1,r,RSON);
	pushup(rt);
}
void update(int l,int r,int rt){
	int mid;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
		if(DATA(rt)!=MAX)DATA(rt)=MAX;
		else DATA(rt)=l;
		return;
	}
	mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)update(l,r,LSON);
	if(mid<r)update(l,r,RSON);
	pushup(rt);
}
int query(int l,int r,int rt){
	int mid,ans=MAX;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r)
	return DATA(rt);
	mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)ans=min(ans,query(l,r,LSON));
	if(mid<r)ans=min(ans,query(l,r,RSON));
	return ans;
}
void work1(int x,int y){
	int s=MAX;
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		s=min(s,query(id[top[x]],id[x],1));
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	s=min(s,query(id[x],id[y],1));
	printf("%d\n",s==MAX?-1:nid[s]);
	return;
}
void work(){
	int f,x;
	while(m--){
		f=read();x=read();
		if(f==0)update(id[x],id[x],1);
		if(f==1)work1(1,x);
	}
}
void init(){
	int x,y;
	n=read();m=read();
	for(int i=1;i<n;i++){
		x=read();y=read();
		add(x,y);
	}
	deep[1]=1;
	dfs1(1);
	dfs2(1,1);
	buildtree(1,n,1);
}
int main(){
	init();
	work();
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值