LOJ #2001. 「SDOI2017」樹點塗色 (LCT + 線段樹)

題面

LOJ 2001

題解

轉自luyixian的博客


操作一

我們發現這一個操作就是把樹中某個節點到根節點的路徑上的所有節點變成一樣的顏色,又因爲這棵樹上有一個性質,同樣顏色的點連接起來一定會是一條鏈,就可以想到LCT的access函數。所以我們將同種顏色的點看成LCT中同一棵splay上的點。

操作二

因爲LCT已經維護了顏色了,所以我們不能再用LCT來維護路徑之間的權值了。於是就可以想到樹上差分,設dis[x]爲點x的權值,這個值就等於在LCT上該點到根節點所經過虛邊的條數+1,x到y路徑的權值就是dis[x]+dis[y]-2*dis[lca]+1,這條公式不多解釋,畫一個圖試試就出來了。

然後,我們就要考慮如何維護dis數組了。在access中,設當前節點爲x,它的父親爲fa(不在同一棵splay中),因爲我們將x和fa中原來的虛邊變成了實邊,所以x爲根節點的子樹中所有節點的dis-1。設son爲fa原來的兒子,那麼就會因爲son和fa中的實邊變成虛邊使得以son爲根節點的子樹中所以節點的dis-1。於是又現這些要+1或-1的節點都在同一棵子樹裏面,就很容易想到樹的dfs序,我們按照這些點的dfs序建一顆線段樹,每次就只需要修改連續的編號即可。

操作三

最複雜的操作二都搞定了,操作三還難嗎,直接在線段樹中求最大值不就行了唄。


CODE

#include <bits/stdc++.h>
using namespace std;
inline void read(int &x) {
	char ch; int flg = 1; while(!isdigit(ch=getchar()))if(ch=='-')flg=-flg;
	for(x=ch-'0';isdigit(ch=getchar());x=x*10+ch-'0'); x*=flg;
}
const int MAXN = 100005;
int n, q, U[MAXN], V[MAXN], fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], cnt;
int ch[MAXN][2], fa[MAXN]; bool tag[MAXN];
inline void add(int u, int v) {
	to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt;
	to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt;
}
int dfn[MAXN], tmr, sz[MAXN], seq[MAXN], son[MAXN], ff[MAXN], dep[MAXN], top[MAXN];
void dfs(int u, int f) {
	dep[u] = dep[fa[u]=ff[u]=f] + (sz[u] = 1);
	seq[dfn[u] = ++tmr] = u;
	for(int i = fir[u], v; i; i = nxt[i])
		if((v=to[i]) != f) {
			dfs(v, u), sz[u] += sz[v];
			if(sz[v] > sz[son[u]]) son[u] = v;
		}
}
void dfs2(int u, int tp) {
	top[u] = tp;
	if(son[u]) dfs2(son[u], tp);
	for(int i = fir[u], v; i; i = nxt[i])
		if((v=to[i]) != ff[u] && v != son[u])
			dfs2(v, v);
}
inline int lca(int u, int v) {
	while(top[u] != top[v]) {
		if(dep[top[u]] > dep[top[v]]) u = ff[top[u]];
		else v = ff[top[v]];
	}
	return dep[u] < dep[v] ? u : v;
}
int lz[MAXN<<2], mx[MAXN<<2];
inline void pd(int i) {
	if(lz[i]) {
		lz[i<<1] += lz[i];
		lz[i<<1|1] += lz[i];
		mx[i<<1] += lz[i];
		mx[i<<1|1] += lz[i];
		lz[i] = 0;
	}
}
inline void upd(int i) { mx[i] = max(mx[i<<1], mx[i<<1|1]); }
void add(int i, int l, int r, int x, int y, int v) {
	if(x <= l && r <= y) {
		lz[i] += v; mx[i] += v; return;
	}
	int mid = (l + r) >> 1; pd(i);
	if(x <= mid) add(i<<1, l, mid, x, y, v);
	if(y > mid) add(i<<1|1, mid+1, r, x, y, v);
	upd(i);
}
int qmx(int i, int l, int r, int x, int y) {
	if(x <= l && r <= y) return mx[i];
	int mid = (l + r) >> 1, re = 0; pd(i);
	if(x <= mid) re = max(re, qmx(i<<1, l, mid, x, y));
	if(y > mid) re = max(re, qmx(i<<1|1, mid+1, r, x, y));
	return re;
}
void build(int i, int l, int r) {
	if(l == r) { mx[i] = dep[seq[l]]; return; }
	int mid = (l + r) >> 1;
	build(i<<1, l, mid);
	build(i<<1|1, mid+1, r);
	upd(i);
}
inline bool isr(int x) { return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
inline bool get(int x) { return ch[fa[x]][1] == x; }
inline void mt(int x) { if(tag[x]) { swap(ch[x][0], ch[0][1]);tag[x] ^= 1;tag[ch[x][0]] ^= 1;tag[ch[x][1]] ^= 1; }}
inline void rot(int x) {
	int y = fa[x], z = fa[y]; bool o = get(x);
	if(!isr(y)) ch[z][get(y)] = x; fa[x] = z;
	ch[y][o] = ch[x][o^1]; if(ch[y][o]) fa[ch[y][o]] = y;
	ch[x][o^1] = y; fa[y] = x;
}
inline void splay(int x) {
	for(; !isr(x); rot(x))
		if(!isr(fa[x])) rot(get(x) == get(fa[x]) ? fa[x] : x);
}
inline void update(int x, int v) {
	if(!x) return; while(ch[x][0]) x=ch[x][0];
	add(1, 1, n, dfn[x], dfn[x]+sz[x]-1, v);
}
inline void access(int x) {
	for(int y = 0; x; x = fa[y=x])
		splay(x), update(ch[x][1], 1), update(ch[x][1]=y, -1);
}
int main () {
	read(n), read(q);
	for(int i = 1, u, v; i < n; ++i) read(u), read(v), add(u, v);
	dfs(1, 0), dfs2(1, 1);
	build(1, 1, n);
	int op, x, y, z;
	while(q--) {
		read(op); read(x);
		if(op == 1) access(x);
		else if(op == 2) {
			read(y); z = lca(x, y);
			int ans = qmx(1,1,n,dfn[x],dfn[x]) + qmx(1,1,n,dfn[y],dfn[y]) - 2*qmx(1,1,n,dfn[z],dfn[z]) + 1;
			printf("%d\n", ans);
		}
		else printf("%d\n", qmx(1, 1, n, dfn[x], dfn[x]+sz[x]-1));
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章