题目:
http://acm.hdu.edu.cn/showproblem.php?pid=4010
题意:
给定一个有 n 个点的树,有以下四种操作:
1 x y: 如果 x y 不在同一棵树上,那么在它们中间新建一条边,把他们连接起来,否则操作非法-
2 x y:
如果
x y
在同一棵树上,那么把
x
替换为树根并且把
y 和 y 的父亲之间的边删掉,否则操作非法 3 w x y: 如果 x y 在同一棵树上,那么把 xy 路径上所有点的点权加上 w ,否则操作非法4 x y: 如果 x y 在同一棵树上,查询 x y 路径上的最大点权,否则操作非法注意,非法操作都输出 −1 ,无论是不是查询
思路:
直接LCT啊。。。我好像老把数据结构写搓,写搓了之后还不会 debug ,真是一场灾难,搞得我怀疑人生。。。
#include <bits/stdc++.h> using namespace std; const int N = 300000 + 10, INF = 0x3f3f3f3f; struct edge { int to, next; }g[N*2]; int cnt, head[N]; int son[N][2], fat[N], key[N], maxval[N], rev[N], lazy[N]; int top, stk[N]; void init() { cnt = 0; memset(head, -1, sizeof head); memset(son, 0, sizeof son); memset(key, 0, sizeof key); memset(fat, 0, sizeof fat); memset(rev, 0, sizeof rev); memset(lazy, 0, sizeof lazy); memset(maxval, 0, sizeof maxval); } void add_edge(int v, int u) { g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++; } void dfs(int v, int fa) { fat[v] = fa; for(int i = head[v]; ~i; i = g[i].next) { int u = g[i].to; if(u == fa) continue; dfs(u, v); } } bool is_root(int x) { return son[fat[x]][0] != x && son[fat[x]][1] != x; } void push_up(int x) { maxval[x] = max(key[x], max(maxval[son[x][0]], maxval[son[x][1]])); } void push_down(int x) { if(lazy[x]) {//注意下放lazy标记时,如果不判断x的左右儿子是否存在而直接下放,相当于是下放到0这个点上,树上有很多点儿子为空都连接0,就会干扰到这些点,导致出现错误。。。 if(son[x][0]) { lazy[son[x][0]] += lazy[x]; key[son[x][0]] += lazy[x]; maxval[son[x][0]] += lazy[x]; } if(son[x][1]) { lazy[son[x][1]] += lazy[x]; key[son[x][1]] += lazy[x]; maxval[son[x][1]] += lazy[x]; } lazy[x] = 0; } if(rev[x]) {//翻转标记不用判断左右儿子是否存在 swap(son[x][0], son[x][1]); rev[son[x][0]] ^= 1, rev[son[x][1]] ^= 1; rev[x] ^= 1; } } void Rotate(int x) { int y = fat[x], p = son[y][0] == x; son[y][!p] = son[x][p], fat[son[x][p]] = y; if(! is_root(y)) son[fat[y]][son[fat[y]][1]==y] = x; fat[x] = fat[y]; son[x][p] = y, fat[y] = x; push_up(y); } void splay(int x) { top = 0; stk[++top] = x; for(int i = x; !is_root(i); i = fat[i]) stk[++top] = fat[i]; for(int i = top; i >= 1; i--) push_down(stk[i]); while(! is_root(x)) { int y = fat[x], z = fat[y]; if(is_root(y)) Rotate(x); else { if((x == son[y][0]) ^ (y == son[z][0])) Rotate(x), Rotate(x); else Rotate(y), Rotate(x); } } push_up(x); } void access(int x) { int y = 0; while(x) { splay(x); son[x][1] = y; push_up(x); y = x, x = fat[x]; } } int find_root(int x) { access(x); splay(x); while(son[x][0]) x = son[x][0]; return x; } bool check(int x, int y) { return find_root(x) == find_root(y); } void make_root(int x) { access(x); splay(x); rev[x] ^= 1; } void link(int x, int y) { if(check(x, y)) { printf("-1\n"); return; } make_root(x); fat[x] = y; } void cut(int x, int y) { if(x == y || !check(x, y)) { printf("-1\n"); return; } make_root(x); access(y); splay(y); son[y][0] = fat[son[y][0]] = 0; push_up(y); } void update(int x, int y, int v) { if(!check(x, y)) { printf("-1\n"); return; } make_root(x); access(y); splay(y); lazy[y] += v, maxval[y] += v, key[y] += v;//此时y的右子树为空,所以可以这样更新 } int query(int x, int y) { if(!check(x, y)) return -1; make_root(x); access(y); splay(y); return maxval[y]; } int main() { int n, m, opt, x, y, z; while(~ scanf("%d", &n)) { init(); for(int i = 1; i <= n-1; i++) { scanf("%d%d", &x, &y); add_edge(x, y); add_edge(y, x); } for(int i = 1; i <= n; i++) scanf("%d", &key[i]), maxval[i] = key[i]; dfs(1, 0); scanf("%d", &m); for(int i = 1; i <= m; i++) { scanf("%d%d%d", &opt, &x, &y); if(opt == 1) link(x, y); else if(opt == 2) cut(x, y); else if(opt == 3) { scanf("%d", &z); update(y, z, x); } else printf("%d\n", query(x, y)); } printf("\n"); } return 0; }
-
2 x y:
如果
x y
在同一棵树上,那么把
x
替换为树根并且把