[LOJ][SDOI2017]树点涂色

链接

(蒟蒻只能够想到 3 操作可以用树剖线段树维护ORZ)
对于每个 1 操作 , 都会覆盖当前节点到根节点的所有颜色 , 且新颜色对所有经过操作节点的路径都有贡献。
所以我们维护所有边的两边颜色是否相同 , 易知在不相同情况下这条边有1的贡献 , 在这种情况下我们就能够对 2 操作进行维护了(考虑差分)

在这里我们用 $ D_{i} $ 表示点 i 到根存在多少这样的边 ,则操作 2 的 答案为 $ Ans = D_{x} + D_{y} - 2 * D_{lca(x , y)} + 1 $

接下来考虑如何维护这些边。

我们会发现覆盖当前节点到根节点的操作与 LCT 中的 Access 操作类似 ,

所以在这里我们就可以用轻重边来表示这些不同的边了!

用 Access 来模拟操作 1 过程 , 重边 - > 轻边 则进行子树加 , 轻边 - > 重边 则进行子树减

由于我们只要维护 LCT 中的 Access , 所以只要对Access操作进行一些修改就行了


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

const int N = 1e5 + 5;

struct Virt{
    double x , y;
    Virt(double _x = 0.0 , double _y = 0.0):x(_x) , y(_y){}
};

Virt operator + (Virt x , Virt y){return Virt(x.x + y.x , x.y + y.y);}
Virt operator - (Virt x , Virt y){return Virt(x.x - y.x , x.y - y.y);}
Virt operator * (Virt x , Virt y){return Virt(x.x * y.x - x.y * y.y , x.x * y.y + x.y * y.x);}

int read(){
	int x = 0 , f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1 ; ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}

int n , Q , opt , x , y;
int num , posin[N] , posout[N] , tot , head[N];
int fa[N] , dep[N] , sz[N] , top[N] , son[N] , tree[N];

struct edge{
	int pos , nx;
}e[N << 1];

void add(int u , int v){
	e[++num].pos = v; e[num].nx = head[u]; head[u] = num;
	e[++num].pos = u; e[num].nx = head[v]; head[v] = num;
}


void dfs1(int now){
	sz[now] = 1;
	for(int i = head[now] ; i ; i = e[i].nx){
		if(e[i].pos == fa[now]) continue;
		fa[e[i].pos] = now; dep[e[i].pos] = dep[now] + 1;
		dfs1(e[i].pos);
		sz[now] += sz[e[i].pos];
		if(sz[e[i].pos] > sz[son[now]]) son[now] = e[i].pos;
	}
}

void dfs2(int now , int father){
	posin[now] = ++tot; tree[tot] = now;
	top[now] = father;
	if(son[now]) dfs2(son[now] , father);
	for(int i = head[now] ; i ; i = e[i].nx){
		if(e[i].pos == son[now] || e[i].pos == fa[now]) continue;
		dfs2(e[i].pos , e[i].pos);
	}
	posout[now] = tot;
}

int lca(int x , int y){
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		x = fa[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

struct SegmentTree{
	struct node{
		int tag , val;
	}tr[N << 2];

	void paint(int k , int d){
		tr[k].tag += d; tr[k].val+=d;
	}

	void push_down(int k){
		paint(k << 1 , tr[k].tag);
		paint(k << 1 | 1 , tr[k].tag);
		tr[k].tag = 0;
	}

	void merge(int k){tr[k].val = max(tr[k << 1].val , tr[k << 1 | 1].val);}

	void build(int k , int l , int r){
		if(l == r) tr[k].val = dep[tree[l]] + 1;
		else build(k << 1 , l , (l + r) >> 1) , build(k << 1 | 1 , ((l + r) >> 1) + 1 , r) , merge(k);
	}

	int query1(int k , int l , int r , int pos){
		if(l == r) return tr[k].val;
		else{
			if(tr[k].tag) push_down(k);
			int mid = (l + r) >> 1;
			if(mid >= pos) return query1(k << 1 , l , mid , pos);
			else return query1(k << 1 | 1, mid + 1 , r , pos);
		}
	}

	int query2(int k , int l , int r , int L , int R){
		if(L <= l && r <= R) return tr[k].val;
		else{
			if(tr[k].tag) push_down(k);
			int mid = (l + r) >> 1 , ans = 0;
			if(mid >= L) ans = max(ans , query2(k << 1 , l , mid , L , R));
			if(mid < R) ans = max(ans , query2(k << 1 | 1 , mid + 1 , r , L , R));
			return ans;
		}
	}

	void change(int k , int  l , int r , int L , int R , int d){
		if(l >= L && r <= R) paint(k , d);
		else{
			if(tr[k].tag) push_down(k);
			int mid = (l + r) >> 1;
			if(mid >= L) change(k << 1 , l , mid , L , R , d);
			if(mid < R) change(k << 1 | 1 , mid + 1 , r , L , R , d);
			merge(k);
		}
	}

}seg;

void addsub(int k , int d){seg.change(1 , 1 , n , posin[k] , posout[k] , d);}
int subquery(int x , int y){
	int temp = lca(x , y);
	//printf("lca : %d\n",temp);
	int ans = seg.query1(1 , 1 , n , posin[x]) + seg.query1(1 , 1 , n , posin[y]) - 2 * seg.query1(1 , 1 , n , posin[temp]) + 1;
	return ans;
}

int maxquery(int k){
	int ans = seg.query2(1 , 1 , n , posin[k] , posout[k]);
	return ans;
}

struct Link_Cut_Tree{
	struct node{int ch[2] , fa;}tr[N];

	int get_son(int x){return tr[tr[x].fa].ch[1] == x;}
	bool is_root(int x){return tr[tr[x].fa].ch[0] != x && tr[tr[x].fa].ch[1] != x;}

	void rotate(int k){
		int f = tr[k].fa , ffa = tr[f].fa , c = get_son(k);
		if(!is_root(f)) tr[ffa].ch[tr[ffa].ch[1] == f] = k; tr[k].fa = ffa;
		tr[f].ch[c] = tr[k].ch[!c]; tr[tr[f].ch[c]].fa = f;
		tr[k].ch[!c] = f; tr[f].fa = k;
	}	

	void splay(int x){
		for(int f ; !is_root(x) ; rotate(x))
			if(!is_root(f = tr[x].fa))
				rotate(get_son(x) == get_son(f) ? f : x);
	}

	int find_root(int x){
		splay(x);
		while(tr[x].ch[0]) x = tr[x].ch[0];
		splay(x);
		return x;
	}	

	void access(int x){
		for(int y = 0 ; x ; y = x , x = tr[x].fa){
			splay(x);
			if(tr[x].ch[1]){
				int temp = tr[x].ch[1]; tr[x].ch[1] = 0;
				temp = find_root(temp); addsub(temp , 1);
			}
			if(y) y = find_root(y) , addsub(y , -1);
			
			tr[x].ch[1] = y;
		}
	}

	void init(){for(int i = 1 ; i <= n ; i++) tr[i].fa = fa[i];};
}lct;

int main(){
	scanf("%d%d",&n,&Q);
	for(int i = 1 ; i < n ; i++) scanf("%d%d",&x,&y) , add(x , y);
	dfs1(1); dfs2(1 , 1); seg.build(1 , 1 , n); lct.init();
	for(int i = 1 ; i <= Q ; i++){
		scanf("%d%d",&opt,&x);
		if(opt == 1) lct.access(x);
		else if(opt == 2) scanf("%d",&y) , printf("%d\n" , subquery(x , y));
		else printf("%d\n" , maxquery(x)); 
	}
	return 0; 
}

posted @ 2018-04-05 02:14  FranceDisco  阅读(153)  评论(0编辑  收藏  举报