@bzoj


@description@

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:

1 x:把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:求x到y的路径的权值。
3 x:在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行m次操作。

Input
第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000

Output
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值

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

@solution@

初看会觉得这道题十分难以下手:对于那个什么“把点x到根结点的路径上所有的点染上一种没有用过的新颜色”这个操作完全没有办法。
但如果仔细思考,外加你数据结构学得非常非常强,可以发现这个其实就是 link-cut-tree 中的 access 操作。

考虑点破了这一点后,我们该如何维护“点到根结点的路径权值(不同颜色段数量)”。
其实你发现,这个就 = 点到根结点经过的 link-cut-tree 中的轻边数量 + 1。
我们考虑在每一次 access 的时候,将所有轻边变重边,重边变轻边的地方作修改。而根据 link-cut-tree 的时间复杂度分析这个修改数量均摊 O(logn)。

实现上,我们可以将 “单边修改+到根的链查询” 变为 “子树修改+单点查询”,转为 dfs 序然后线段树维护。
对于询问 2 就只需要查询出 lca 然后变成 f(u) + f(v) - 2*f(lca)。
对于询问 3,将树转为 dfs 序后就在 dfs 序上查询最大值。

时间复杂度 O(nlog^2n),瓶颈卡在操作 1 的 access + 线段树上修改,其他两个询问每一次是 O(logn)。

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
int dfn[MAXN + 5], tid[MAXN + 5], siz[MAXN + 5], dep[MAXN + 5], fa[20][MAXN + 5], dcnt = 0;
struct Segtree{
	struct node{
		int l, r;
		int tg, mx;
	}t[4*MAXN + 5];
	void pushdown(int x) {
		if( t[x].tg ) {
			t[x<<1].tg += t[x].tg, t[x<<1].mx += t[x].tg;
			t[x<<1|1].tg += t[x].tg, t[x<<1|1].mx += t[x].tg;
			t[x].tg = 0;
		}
	}
	void pushup(int x) {
		t[x].mx = max(t[x<<1].mx, t[x<<1|1].mx);
	}
	void build(int x, int l, int r) {
		t[x].l = l, t[x].r = r, t[x].tg = 0, t[x].mx = 0;
		if( l == r ) {
			t[x].mx = dep[dfn[l]] - 1;
			return ;
		}
		int mid = (l + r) >> 1;
		build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
		pushup(x);
	}
	void modify(int x, int l, int r, int d) {
		if( l > t[x].r || r < t[x].l )
			return ;
		if( l <= t[x].l && t[x].r <= r ) {
			t[x].tg += d, t[x].mx += d;
			return ;
		}
		pushdown(x);
		modify(x << 1, l, r, d);
		modify(x << 1 | 1, l, r, d);
		pushup(x);
	}
	int query(int x, int l, int r) {
		if( l > t[x].r || r < t[x].l )
			return 0;
		if( l <= t[x].l && t[x].r <= r )
			return t[x].mx;
		pushdown(x);
		return max(query(x << 1, l, r), query(x << 1 | 1, l, r));
	}
}T1;
struct Link_Cut_Tree{
	struct node{node *ch[2], *fa;}pl[MAXN + 5], *NIL;
	Link_Cut_Tree() {
		NIL = &pl[0];
		NIL->ch[0] = NIL->ch[1] = NIL->fa = NIL;
	}
	bool is_root(node *x) {
		return x->fa->ch[0] != x && x->fa->ch[1] != x;
	}
	void set_child(node *x, node *y, int d) {
		if( x != NIL ) x->ch[d] = y;
		if( y != NIL ) y->fa = x;
	}
	void rotate(node *x) {
		node *y = x->fa; int d = (y->ch[1] == x);
		if( is_root(y) ) x->fa = y->fa;
		else set_child(y->fa, x, (y->fa->ch[1] == y));
		set_child(y, x->ch[!d], d);
		set_child(x, y, !d);
	}
	void splay(node *x) {
		while( !is_root(x) ) {
			node *y = x->fa;
			if( is_root(y) )
				rotate(x);
			else {
				if( (y->fa->ch[1] == y) == (y->ch[1] == x) )
					rotate(y);
				else rotate(x);
				rotate(x);
			}
		}
	}
	node *find(node *x) {
		node *ret = x;
		while( ret->ch[0] != NIL )
			ret = ret->ch[0];
		splay(ret);
		return ret;
	}
	void access(node *x) {
		node *y = NIL;
		while( x != NIL ) {
			splay(x);
			node *k = x->ch[1];
			x->ch[1] = y;
			if( k != NIL ) {
				k = find(k);
				T1.modify(1, tid[k-pl], tid[k-pl]+siz[k-pl]-1, 1);
			}
			x = find(x);
			if( x->fa != NIL )
				T1.modify(1, tid[x-pl], tid[x-pl]+siz[x-pl]-1, -1);
			y = x, x = x->fa;
		}
	}
}T2;
Link_Cut_Tree::node *nd[MAXN + 5];
struct edge{
	edge *nxt; int to;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
	edge *p = (++ecnt);
	p->to = v, p->nxt = adj[u], adj[u] = p;
	p = (++ecnt);
	p->to = u, p->nxt = adj[v], adj[v] = p;
}
void dfs(int x, int f) {
	dfn[++dcnt] = x, tid[x] = dcnt, dep[x] = dep[f] + 1, siz[x] = 1, fa[0][x] = f;
	for(int i=1;i<20;i++)
		fa[i][x] = fa[i-1][fa[i-1][x]];
	for(edge *p=adj[x];p;p=p->nxt) {
		if( p->to == f ) continue;
		dfs(p->to, x); siz[x] += siz[p->to];
	}
}
int lca(int u, int v) {
	if( dep[u] < dep[v] ) swap(u, v);
	for(int i=19;i>=0;i--)
		if( dep[fa[i][u]] >= dep[v] )
			u = fa[i][u];
	if( u == v ) return u;
	for(int i=19;i>=0;i--)
		if( fa[i][u] != fa[i][v] )
			u = fa[i][u], v = fa[i][v];
	return fa[0][u];
}
int main() {
	int n, m; scanf("%d%d", &n, &m);
	for(int i=1;i<n;i++) {
		int a, b; scanf("%d%d", &a, &b);
		addedge(a, b);
	}
	for(int i=1;i<=n;i++)
		nd[i] = &T2.pl[i], nd[i]->fa = nd[i]->ch[0] = nd[i]->ch[1] = T2.NIL;
	dfs(1, 0);
	for(int i=2;i<=n;i++)
		nd[i]->fa = nd[fa[0][i]];
	T1.build(1, 1, n);
	for(int i=1;i<=m;i++) {
		int op; scanf("%d", &op);
		if( op == 1 ) {
			int x; scanf("%d", &x);
			T2.access(nd[x]);
		}
		else if( op == 2 ) {
			int x, y; scanf("%d%d", &x, &y);
			int l = lca(x, y);
			printf("%d
", T1.query(1, tid[x], tid[x]) + T1.query(1, tid[y], tid[y]) - 2*T1.query(1, tid[l], tid[l]) + 1);
		}
		else {
			int x; scanf("%d", &x);
			printf("%d
", T1.query(1, tid[x], tid[x]+siz[x]-1) + 1);
		}
	}
}

@details@

感觉这个思想还是挺有意思的(虽然最近好像就莫名其妙变成了一种套路?)

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11337623.html