[SCOI2011]棘手的操作

Description
  有 N N 个节点,标号从1 N N ,这N个节点一开始相互不连通。第i个节点的初始权值为 a[i] a [ i ] ,接下来有如下一些操作:

   U U x y y : 加一条边,连接第x个节点和第 y y 个节点
  A1 x x v: 将第 x x 个节点的权值增加v
   A2 A 2 x x v: 将第 x x 个节点所在的连通块的所有节点的权值都增加v
   A3 A 3 v v : 将所有节点的权值都增加v
   F1 F 1 x x : 输出第x个节点当前的权值
   F2 F 2 x x : 输出第x个节点所在的连通块中,权值最大的节点的权值
   F3 F 3 : 输出所有节点中,权值最大的节点的权值

Input
  第一行包含 2 2 个正整数n q q ,分别表示城市的数量和旅行者数量。
  第二行包含n个非负整数,其中第 i i 个整数Gi表示 i i 号城市的幸运值。
  随后n1行,每行包含两个正整数 x x y,表示 x x 号城市和y号城市之间有一条道路相连。
  随后 q q 行,每行包含两个正整数x y y 表示这名旅行者的旅行计划是从x号城市到 y y 号城市
  N20000, Q200000 Q ≤ 200000 , Gi260 G i ≤ 2 60

Output
  输出需要包含 q q 行,每行包含1个非负整数,表示这名旅行者可以保留的最大幸运值。

Solution
  为了巩固左偏树而写的题目,由于懒得写左偏套左偏就直接下一维用 multiset m u l t i s e t 弄了,(你不是说要巩固左偏树吗怎么能这样!!!
  我还有一个小问题,就是到底左偏树的的任意节点到根的路径长是多少啊,网上有说理论复杂度 O(n) O ( n ) 的,然而那位大佬自己的代码也是一个一个往上跳的,并且我也没 T T <script type="math/tex" id="MathJax-Element-275">T</script>掉,然而还是非常懵逼,留坑???

Source

//2018-4-22
//miaomiao
//
#include <bits/stdc++.h>
using namespace std;

#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define N (300000 + 5)

struct node{
    int o, lc, rc, dis, mv, sv, tag;
}tr[N];

int Mx, n, Q, rt[N], fa[N], f[N], tp, st[N];
multiset<int> all;

void Del(int now){
    multiset<int>::iterator t = all.lower_bound(now);
    all.erase(t);
}

inline void Pushdown(int a){
    if(!tr[a].tag) return;
    int lc = tr[a].lc, rc = tr[a].rc, ad = tr[a].tag;

    if(lc) tr[lc].tag += ad, tr[lc].mv += ad, tr[lc].sv += ad;
    if(rc) tr[rc].tag += ad, tr[rc].mv += ad, tr[rc].sv += ad;

    tr[a].tag = 0;
}

inline void Pushup(int a){
    tr[a].sv = tr[a].mv;
    if(tr[a].lc) tr[a].sv = max(tr[a].sv, tr[tr[a].lc].sv);
    if(tr[a].rc) tr[a].sv = max(tr[a].sv, tr[tr[a].rc].sv);
}

int Merge(int a, int b){
    if(!a || !b) return a + b;

    if(tr[a].o > tr[b].o) swap(a, b);
    fa[a] = 0;

    Pushdown(a);
    tr[a].rc = Merge(tr[a].rc, b);
    Pushup(a);

    if(tr[tr[a].lc].dis < tr[tr[a].rc].dis) swap(tr[a].lc, tr[a].rc);
    if(tr[a].lc) fa[tr[a].lc] = a; if(tr[a].rc) fa[tr[a].rc] = a;

    if(!tr[a].rc) tr[a].dis = 0;
    else tr[a].dis = tr[tr[a].rc].dis + 1;

    return a;
}

void Down(int a){
    tp = 0;
    for(int i = a; i; i = fa[i]) st[++tp] = i;
    while(tp) Pushdown(st[tp--]);
}

void Set(int a, int av){
    Down(a);

    tr[a].mv += av;
    while(fa[a]){
        Pushup(a); a = fa[a];   
    }

    Del(tr[a].sv); Pushup(a);
    all.insert(tr[a].sv);
}

void Tag(int a, int ad){
    Del(tr[a].sv);
    tr[a].tag += ad, tr[a].mv += ad, tr[a].sv += ad;
    all.insert(tr[a].sv);
}

inline int Find(int x){return x == f[x]? x: (f[x] = Find(f[x]));}
void Unit(int u, int v){
    int x = Find(u), y = Find(v);
    if(x == y) return;

    if(y < x) swap(x, y);

    if(tr[x].sv < tr[y].sv) Del(tr[x].sv);
    else Del(tr[y].sv);

    f[y] = x; Merge(rt[x], rt[y]);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("operation.in", "r", stdin);
    freopen("operation.out", "w", stdout);
#endif

    int a;

    scanf("%d", &n);
    For(i, 1, n){
        scanf("%d", &a);

        f[i] = i;
        tr[rt[i] = i] = (node){i, 0, 0, 0, a, a, 0};
        all.insert(a);
    }

    char op[5];
    int u, v, add = 0;

    scanf("%d", &Q);

    while(Q --){
        scanf("%s", op);

        if(op[0] == 'U'){
            scanf("%d%d", &u, &v); Unit(u, v);

        }else if(op[0] == 'A'){
            if(op[1] == '1'){
                scanf("%d%d", &u, &v); Set(u, v);

            }else if(op[1] == '2'){
                scanf("%d%d", &u, &v); Tag(Find(u), v);

            }else{
                scanf("%d", &v); add += v;
            }

        }else{
            if(op[1] == '1'){
                scanf("%d", &u);
                Down(u); printf("%d\n", tr[u].mv + add);

            }else if(op[1] == '2'){
                scanf("%d", &u);
                printf("%d\n", tr[Find(u)].sv + add);

            }else printf("%d\n", (*all.rbegin()) + add);
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值