[Sdoi2017]树点涂色 LCT+线段树

Description
Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作


Sample Input
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5


Sample Output
3
4
2
2


一开始易得一个想法,s[x]表示根到x不一样的颜色数。
对于第二个询问,既然他每次染的颜色都是不一样的,那你就可以直接用s[x]+s[y]-2*s[lca]+1即可。
对于第三个询问,直接输出max。
然后就要考虑修改,其实也比较容易想到,对于一个点往上走,一遇到不同的颜色,就修改,全部修改一遍时间复杂度可以均摊到mlogn。
那么该怎么搞呢。。。
一开始的想法是一个并查集,但想想就很fake。。。
膜了题解,好吧,是个LCT
你每次然一段色其实就相当于access一下,每次一条虚边相连就说明上下两段颜色不同,然后你access上去时修改一下即可。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
inline int read() {
    int s = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * f;
}

struct tnode {
	int son[2], f;
} t[110000];
struct node {
	int l, r, lc, rc, c, lazy;
} tr[210000]; int cnt;
int id, ll[110000], rr[110000];
struct edge {
	int x, y, next;
} e[210000]; int len, last[110000];
int dep[110000], fa[20][110000];

void ins(int x, int y) {
	e[++len].x = x; e[len].y = y;
	e[len].next = last[x]; last[x] = len;
}

bool hh(int x) {
	int f = t[x].f;
	if(f == 0 || (t[f].son[0] != x && t[f].son[1] != x)) return 0;
	return 1;
}

void rotate(int x, int fx) {
	int f = t[x].f, ff = t[f].f;
	int r, R;
	
	r = t[x].son[fx], R = f;
	t[R].son[1 ^ fx] = r;
	if(r) t[r].f = R;
	
	r = x, R = ff;
	if(t[R].son[0] == f) t[R].son[0] = r; else if(t[R].son[1] == f) t[R].son[1] = r;
	t[r].f = R;
	
	r = f, R = x;
	t[R].son[fx] = r;
	t[r].f = R;
}

void splay(int x) {
	while(hh(x)) {
		int f = t[x].f, ff = t[f].f;
		if(!hh(f)) {
			if(t[f].son[0] == x) rotate(x, 1);
			else if(t[f].son[1] == x) rotate(x, 0);
		}
		else if(t[f].son[0] == x && t[ff].son[0] == f) rotate(f, 1), rotate(x, 1);
		else if(t[f].son[0] == x && t[ff].son[1] == f) rotate(x, 1), rotate(x, 0);
		else if(t[f].son[1] == x && t[ff].son[1] == f) rotate(f, 0), rotate(x, 0);
		else if(t[f].son[1] == x && t[ff].son[0] == f) rotate(x, 0), rotate(x, 1);
	}
}

void bt(int l, int r) {
	int now = ++cnt;
	tr[now].l = l, tr[now].r = r;
	tr[now].lc = tr[now].rc = -1;
	tr[now].c = 0;
	if(l < r) {
		int mid = (l + r) / 2;
		tr[now].lc = cnt + 1; bt(l, mid);
		tr[now].rc = cnt + 1; bt(mid + 1, r);
	}
}

void Lazy(int now) {
	if(!tr[now].lazy) return ;
	int lc = tr[now].lc, rc = tr[now].rc;
	tr[lc].c += tr[now].lazy, tr[rc].c += tr[now].lazy;
	tr[lc].lazy += tr[now].lazy, tr[rc].lazy += tr[now].lazy;
	tr[now].lazy = 0;
}

void change(int now, int l, int r, int c) {
	if(tr[now].l == l && tr[now].r == r) {
		tr[now].c += c;
		tr[now].lazy += c;
		return ;
	} Lazy(now);
	int lc = tr[now].lc, rc = tr[now].rc;
	int mid = (tr[now].l + tr[now].r) / 2;
	if(r <= mid) change(lc, l, r, c);
	else if(l > mid) change(rc, l, r, c);
	else change(lc, l, mid, c), change(rc, mid + 1, r, c);
	tr[now].c = _max(tr[lc].c, tr[rc].c);
}

int findmax(int now, int l, int r) {
	if(tr[now].l == l && tr[now].r == r) return tr[now].c;
	Lazy(now);
	int lc = tr[now].lc, rc = tr[now].rc;
	int mid = (tr[now].l + tr[now].r) / 2;
	if(r <= mid) return findmax(lc, l, r);
	else if(l > mid) return findmax(rc, l, r);
	else return _max(findmax(lc, l, mid), findmax(rc, mid + 1, r));
}

int getrt(int x) {
	while(t[x].son[0]) x = t[x].son[0];
	return x;
}

void access(int x) {
	int y = 0;
	while(x) {
		splay(x);
		if(t[x].son[1]) {
			int hh = getrt(t[x].son[1]);
			change(1, ll[hh], rr[hh], 1);
		} t[x].son[1] = y;
		if(y) {
			t[y].f = x;
			int hh = getrt(y);
			change(1, ll[hh], rr[hh], -1);
		} y = x; x = t[x].f;
	}
}

void dfs(int x) {
	t[x].f = fa[0][x];
	for(int i = 1; (1 << i) <= dep[x]; i++) fa[i][x] = fa[i - 1][fa[i - 1][x]];
	ll[x] = ++id;
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(y != fa[0][x]) fa[0][y] = x, dep[y] = dep[x] + 1, dfs(y);
	} rr[x] = id;
}

int LCA(int x, int y) {
	if(dep[x] > dep[y]) swap(x, y);
	for(int i = 18; i >= 0; i--) if(dep[y] - dep[x] >= (1 << i)){
		y = fa[i][y];
	} if(x == y) return x;
	for(int i = 18; i >= 0; i--) if(fa[i][x] != fa[i][y]){
		x = fa[i][x], y = fa[i][y];
	} return fa[0][x];
}

int main() {
	int n = read(), m = read();
	for(int i = 1; i < n; i++) {
		int x  = read(), y = read();
		ins(x, y), ins(y, x);
	} dep[1] = 0; dfs(1); bt(1, n);
	for(int i = 1; i <= n; i++) change(1, ll[i], ll[i], dep[i]);
	for(int i = 1; i <= m; i++) {
		int opt = read(), x = read(), y;
		if(opt == 1) access(x);
		else if(opt == 2) {
			y = read();
			int lca = LCA(x, y);
			int a = findmax(1, ll[x], ll[x]), b = findmax(1, ll[y], ll[y]), c = findmax(1, ll[lca], ll[lca]);
			printf("%d\n", a + b - c * 2 + 1);
		} else printf("%d\n", findmax(1, ll[x], rr[x]) + 1);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值