「CF343D」树链剖分

D. Water Tree

time limit per test 4 seconds
memory limit per test 256 megabytes
input standard input
output standard output
Mad scientist Mike has constructed a rooted tree, which consists of n vertices. Each vertex is a reservoir which can be either empty or filled with water.

The vertices of the tree are numbered from 1 1 1 to n with the root at vertex 1 1 1. For each vertex, the reservoirs of its children are located below the reservoir of this vertex, and the vertex is connected with each of the children by a pipe through which water can flow downwards.

Mike wants to do the following operations with the tree:

  • Fill vertex v v v with water. Then v and all its children are filled with water.
  • Empty vertex v v v. Then v v v and all its ancestors are emptied.
  • Determine whether vertex v v v is filled with water at the moment.

Initially all vertices of the tree are empty.
Mike has already compiled a full list of operations that he wants to perform in order. Before experimenting with the tree Mike decided to run the list through a simulation. Help Mike determine what results will he get after performing all the operations.

Input

The first line of the input contains an integer n   ( 1   ≤   n   ≤   500000 ) n\ (1 ≤ n ≤ 500000) n (1n500000) — the number of vertices in the tree. Each of the following n   −   1 n - 1 n1 lines contains two space-separated numbers a i , b i ( 1   ≤   a i ,   b i   ≤   n , a i   ! =   b i ) a_i, b_i (1 ≤ a_i, b_i ≤ n, a_i != b_i) ai,bi(1ai,bin,ai!=bi) — the edges of the tree.

The next line contains a number q ( 1   ≤   q   ≤   500000 ) q (1 ≤ q ≤ 500000) q(1q500000) — the number of operations to perform. Each of the following q lines contains two space-separated numbers c i ( 1   ≤   c i   ≤   3 ) , v i ( 1   ≤   v i   ≤   n ) c_i (1 ≤ c_i ≤ 3), v_i (1 ≤ v_i ≤ n) ci(1ci3),vi(1vin), where c i c_i ci is the operation type (according to the numbering given in the statement), and v i v_i vi is the vertex on which the operation is performed.

It is guaranteed that the given graph is a tree.

Output

For each type 3 3 3 operation print 1 1 1 on a separate line if the vertex is full, and 0 if the vertex is empty. Print the answers to queries in the order in which the queries are given in the input.

Examples

input
5
1 2
5 1
2 3
4 2
12
1 1
2 3
3 1
3 2
3 3
3 4
1 2
2 4
3 1
3 3
3 4
3 5
output
0
0
0
1
0
1
0
1
  • 题意

    就是给你一颗根节点为1的树,开始所有节点权值为0,需要支持的操作有:
    • 将以 v v v为根节点的子树的所有节点的权值更新为 1 1 1
    • 将从 1 1 1 v v v的最短路径上的所有点的权值更新为 0 0 0
    • 查询节点 v v v的权值
  • 做法

    • 没想到 d i v 1 div1 div1也会出模板题2333

    • 首先你可能会想着用一个dfs序加一个树状数组或者线段树维护一下子树的修改,但是链呢?自然的想到树链剖分,刚好剖出来的子树编号也是连续的,然后这题实际上是在考你线段树而不是树链剖分,线段树维护的时候也许你会打两个标记(但是这样好像并不行?因为两个标记的话同一个节电可能存在两个标记,那么你先 d o w n down down哪一个呢?)所以考虑一个 m a r k mark mark,其含义如下:

      • − 1 -1 1:该区间没有操作
      • 0 0 0:该区间全部清零
      • 1 1 1:该区间全部变成1

      然后再用一个 v a l val val记录当前区间的值是否单一,因为如果都是一样的话你就不用继续 D F S DFS DFS了,直接返回当前区间的值就行了, v a l val val含义如下:

      • − 1 -1 1:该区间既有 0 0 0又有 1 1 1
      • 0 0 0:该区间全部都为 0 0 0
      • 1 1 1:该区间全部都为 1 1 1
  • 附代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=5e5+10;

vector<int> vec[maxn];
int n,q,u,v,opt;
int h[maxn],fa[maxn],top[maxn],id[maxn],son[maxn],siz[maxn],rk[maxn],tot;
struct node{
	int l,r,val,mark; //val:-1:混合 0:没有水 1:有水 									
}tree[maxn<<2];       //mark:-1无操作 0:清0  1:区间全部加水

struct segment_tree{
	void build(int le,int ri,int id){
		tree[id].l=le,tree[id].r=ri,tree[id].val=0,tree[id].mark=-1;
		if(le==ri) return;
		int mid=(le+ri)>>1;
		build(le,mid,id<<1);
		build(mid+1,ri,id<<1|1);
		pushup(id);
	}

	void pushup(int id){
		if(tree[id<<1].val==tree[id<<1|1].val) tree[id].val=tree[id<<1].val;
		else tree[id].val=-1;
	}

	void down(int id){
		tree[id<<1].mark=tree[id<<1|1].mark=tree[id].mark;
		tree[id<<1].val=tree[id<<1|1].val=tree[id].mark;
		tree[id].mark=-1;
	}

	int len(int id){
		return tree[id].r-tree[id].l+1;
	}

	void update(int l,int r,int id,int opt){
		if(l<=tree[id].l&&r>=tree[id].r){
			tree[id].mark=opt;
			if(opt) tree[id].val=1;
			else tree[id].val=0;
			return;
		}
		if(tree[id].mark!=-1) down(id);
		int mid=(tree[id].l+tree[id].r)>>1;
		if(l<=mid) update(l,r,id<<1,opt);
		if(r>mid) update(l,r,id<<1|1,opt);
		pushup(id);
	}

	int query(int k,int id){
		int ans=-1;
		if(tree[id].l==tree[id].r) return tree[id].val;

		if(tree[id].val!=-1) return tree[id].val;
		if(tree[id].mark!=-1) down(id);

		int mid=(tree[id].l+tree[id].r)>>1;
		if(k<=mid) ans=query(k,id<<1);
		else ans=query(k,id<<1|1);

		pushup(id);
		return ans;
	}
}dat;

struct tree{
	tree(){
		tot=0;
		memset(son,0,sizeof(son));
		memset(h,0,sizeof(h));
	}

	void build(){
		dfs1(1,0,1);dfs2(1,1);
		dat.build(1,n,1);
	}

	void dfs1(int cur,int fath,int he){ //dfs(root,0,1)
 		h[cur]=he;fa[cur]=fath;siz[cur]=1;
 		for(int i=0;i<vec[cur].size();i++){
 			if(vec[cur][i]!=fath){
 				dfs1(vec[cur][i],cur,he+1);
 				siz[cur]+=siz[vec[cur][i]];
 				if(siz[vec[cur][i]]>siz[son[cur]]) son[cur]=vec[cur][i];
 			}
		}
	}

	void dfs2(int cur,int topp){ //dfs2(root,root)
		id[cur]=++tot;rk[tot]=cur;top[cur]=topp;
		if(son[cur]) dfs2(son[cur],topp);
		for(int i=0;i<vec[cur].size();i++){
			if(vec[cur][i]!=fa[cur]&&vec[cur][i]!=son[cur]){
				dfs2(vec[cur][i],vec[cur][i]);
			}
		}
	}

	int query(int x){
		return dat.query(id[x],1);
	}

	void update(int x,int y){
		int topx=top[x],topy=top[y];
		while(topx!=topy){
			if(h[topx]>=h[topy]){
				dat.update(id[topx],id[x],1,0);
				x=fa[topx],topx=top[x];
			}else{
				dat.update(id[topy],id[y],1,0);
				y=fa[topy],topy=top[y];
			}
		}
		dat.update(min(id[x],id[y]),max(id[x],id[y]),1,0);
	}
	void update_son_tree(int x){
		dat.update(id[x],id[x]+siz[x]-1,1,1);
	}
}chain;



int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		scanf("%d %d",&u,&v);
		vec[u].push_back(v);
		vec[v].push_back(u);
	}

	chain.build();
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		scanf("%d %d",&opt,&u);
		if(opt==1){
			chain.update_son_tree(u);
		}else if(opt==2){
			chain.update(1,u);
		}else printf("%d\n",chain.query(u));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值