1. 程式人生 > >[BZOJ 3306]樹

[BZOJ 3306]樹

題目描述

給定一棵大小為 n 的有根點權樹,支援以下操作:

• 換根

• 修改點權

• 查詢子樹最小值

輸入輸出格式

輸入格式

第一行兩個整數 n,Q ,分別表示樹的大小和運算元。

接下來n行,每行兩個整數f,v,第i+1行的兩個數表示點i的父親和點i的權。保證f<i。如 果f=0,那麼i為根。輸入資料保證只有i=1時,f=0

接下來 m 行,為以下格式中的一種:

Vxy表示把點x的權改為y

Ex 表示把有根樹的根改為點 x

Qx 表示查詢點 x 的子樹最小值

輸出格式

對於每個 Q ,輸出子樹最小值。

輸入輸出樣例

輸入樣例#1

3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1

輸出樣例#1

1
2
3
4

提示

對於 100% 的資料:n,Q105

解題分析

這道題看起來應該是樹剖換根操作, 但也可以用LCT維護有根樹。 對於換根操作, 直接makeroot即可。

那麼由於是有根樹, 我們不能隨便split。 我們注意到access後的點是沒有兒子的, 子樹內的點都位於虛邊上面。 我們可以用一個

multiset維護虛邊資訊, 每次accesssplay,輸出堆中最小元素即可。

程式碼如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#include <set>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) #define MX 100050 bool neg; template <class T> IN void in(T &x) { x = 0; R char c = gc; for (; !isdigit(c); c = gc) if(c == '-') neg = true; for (; isdigit(c); c = gc) x = (x << 1) + (x << 3) + c - 48; if(neg) neg = false, x = -x; } int dot, q, cnt, top; int sta[MX], head[MX]; struct Edge {int to, nex;} edge[MX << 1]; struct Node { int son[2], fat, val, mn; bool rev; std::multiset <int> st; }tree[MX]; IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;} namespace LCT { #define dad tree[now].fat #define ls tree[now].son[0] #define rs tree[now].son[1] IN bool get(R int now) {return tree[dad].son[1] == now;} IN bool nroot(R int now) {return tree[dad].son[0] == now || tree[dad].son[1] == now;} IN void pushup(R int now) { tree[now].mn = *tree[now].st.begin(); if(ls) tree[now].mn = min(tree[now].mn, tree[ls].mn); if(rs) tree[now].mn = min(tree[now].mn, tree[rs].mn); } IN void pushrev(R int now) {tree[now].rev ^= 1; std::swap(ls, rs);} IN void pushdown(R int now) {if(tree[now].rev) pushrev(ls), pushrev(rs), tree[now].rev = false;} IN void rotate(const int &now) { R bool dir = get(now); R int fa = dad, grand = tree[fa].fat; tree[fa].son[dir] = tree[now].son[dir ^ 1]; tree[tree[now].son[dir ^ 1]].fat = fa; if(nroot(fa)) tree[grand].son[get(fa)] = now; tree[now].fat = grand; tree[now].son[dir ^ 1] = fa; tree[fa].fat = now; pushup(fa); } IN void splay(int now) { top = 0; R int x = now, fa, grand; sta[++top] = x; W (nroot(x)) x = tree[x].fat, sta[++top] = x; W (top) pushdown(sta[top--]); W (nroot(now)) { fa = dad, grand = tree[fa].fat; if(nroot(fa)) rotate(get(fa) == get(now) ? fa : now); rotate(now); } pushup(now); } IN void access(R int now) { for (R int x = 0; now; x = now, now = dad) { splay(now); if(rs) tree[now].st.insert(tree[rs].mn); if(rs = x) tree[now].st.erase(tree[now].st.find(tree[x].mn)); pushup(now); } } IN void makeroot(R int now) {access(now); splay(now); pushrev(now);} } void DFS(R int now) { tree[now].st.insert(tree[now].val); for (R int i = head[now]; i; i = edge[i].nex) DFS(edge[i].to), tree[now].st.insert(tree[edge[i].to].mn); LCT::pushup(now); } int main(void) { char buf[3]; int a, b; in(dot), in(q); for (R int i = 1; i <= dot; ++i) { in(a), in(tree[i].val); if(!a) continue; add(a, i); tree[i].fat = a; } DFS(1); W (q--) { scanf("%s", buf); if(buf[0] == 'Q') { in(a), LCT::access(a), LCT::splay(a); printf("%d\n", *tree[a].st.begin()); } else if(buf[0] == 'V') { in(a), in(b), LCT::access(a), LCT::splay(a); tree[a].st.erase(tree[a].st.find(tree[a].val)); tree[a].st.insert(b), tree[a].val = b; LCT::pushup(a); } else in(a), LCT::makeroot(a); } }