[BZOJ 3306]树

BZOJ传送门

题目描述

给定一棵大小为 n n 的有根点权树,支持以下操作:

• 换根

• 修改点权

• 查询子树最小值

输入输出格式

输入格式

第一行两个整数 n,Q ,分别表示树的大小和操作数。

接下来 n n 行,每行两个整数f,v,第 i+1 i + 1 行的两个数表示点 i i 的父亲和点i的权。保证 f<i f < i 。如 果 f=0 f = 0 ,那么 i i 为根。输入数据保证只有i=1时, f=0 f = 0

接下来 m m 行,为以下格式中的一种:

V x y表示把点 x x 的权改为y

E x E   x 表示把有根树的根改为点 x x

Q x 表示查询点 x 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 n , Q ≤ 10 5

解题分析

这道题看起来应该是树剖换根操作, 但也可以用 LCT L C T 维护有根树。 对于换根操作, 直接 makeroot m a k e r o o t 即可。

那么由于是有根树, 我们不能随便 split s p l i t 。 我们注意到 access a c c e s s 后的点是没有儿子的, 子树内的点都位于虚边上面。 我们可以用一个 multiset m u l t i s e t 维护虚边信息, 每次 access a c c e s s splay s p l a y ,输出堆中最小元素即可。

代码如下:

#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);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值