[BZOJ 3306]樹
阿新 • • 發佈:2018-12-09
題目描述
給定一棵大小為 的有根點權樹,支援以下操作:
• 換根
• 修改點權
• 查詢子樹最小值
輸入輸出格式
輸入格式
第一行兩個整數 ,分別表示樹的大小和運算元。
接下來行,每行兩個整數,第行的兩個數表示點的父親和點的權。保證。如 果,那麼為根。輸入資料保證只有時,。
接下來 行,為以下格式中的一種:
• 表示把點的權改為
• 表示把有根樹的根改為點
• 表示查詢點 的子樹最小值
輸出格式
對於每個 ,輸出子樹最小值。
輸入輸出樣例
輸入樣例#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% 的資料:。
解題分析
這道題看起來應該是樹剖換根操作, 但也可以用維護有根樹。 對於換根操作, 直接即可。
那麼由於是有根樹, 我們不能隨便。 我們注意到後的點是沒有兒子的, 子樹內的點都位於虛邊上面。 我們可以用一個維護虛邊資訊, 每次再,輸出堆中最小元素即可。
程式碼如下:
#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);
}
}