zoukankan      html  css  js  c++  java
  • 【SCOI 2011】棘手的操作

    Solution

    感觉这题的思路有点神。(有种树套树的赶脚)

    首先我们可以开一颗左偏树维护每个连通块的最大值,再开一颗左偏树来维护每个连通块的最大值的最大值(就是将每个连通块的最大值代表的节点再建一颗左偏树)。

    这样我们就可以解决第七个问题了。

    详见注释。

    P.S. 感觉代码的时间复杂度很不 ok 呢,并查集没有用路径压缩,最坏情况是 (mathtt{O(n)}) 级别的复杂度啊???还有常数似乎有点大???反正 (mathtt{O(玄学)})(mathtt{O(可过)}) 就对了。。。

    Code

    #include <queue>
    #include <cstdio>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    
    const int N = 3e5 + 5;
    
    int n;
    queue <int> q;
    
    int read() {
        int x = 0, f = 1; char s;
        while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
        while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
        return x * f;
    }
    
    struct LT {
        int dis[N], val[N], la[N], f[N], son[N][2];
    
        void sign(const int o, const int Add) {
            if(! o) return;
            val[o] += Add; la[o] += Add;
        }
    
        int Find(int x) {
            while(f[x]) x = f[x];
            return x;
        }
    
        void pushDown(const int o) {
            if(! o) return;
            sign(son[o][0], la[o]);
            sign(son[o][1], la[o]);
            la[o] = 0;
        }
    
        int unite(int x, int y) {
            if(! x || ! y) return x | y;
            if(val[x] < val[y]) swap(x, y);
            pushDown(x);
            son[x][1] = unite(son[x][1], y); f[son[x][1]] = x;
            if(dis[son[x][0]] < dis[son[x][1]]) swap(son[x][0], son[x][1]);
            dis[x] = dis[son[x][1]] + 1;
            return x;
        }
    
        void Clear(const int x) {f[x] = son[x][0] = son[x][1] = 0;}
    
        int get(int x) {
            int ans = val[x];
            for(x = f[x]; x; x = f[x]) ans += la[x];
            return ans;
        }
    
        int del(const int x) {
            pushDown(x);
            int t = unite(son[x][0], son[x][1]), fa = f[x];
            f[t] = fa;
            if(fa) son[fa][x == son[fa][1]] = t;
            Clear(x);
            while(fa) {//维护树的形态
                if(dis[son[fa][0]] < dis[son[fa][1]]) swap(son[fa][0], son[fa][1]);
                if(dis[fa] == dis[son[fa][1]] + 1) return Find(fa);
                dis[fa] = dis[son[fa][1]] + 1;
                t = fa; fa = f[fa];
            }
            return t;
        }
    
        int add(const int u, const int k) {
            val[u] += k;
            pushDown(f[u]); pushDown(u);//传递父亲的权值就可以比较父亲和自己,传递自己的权值可以比较自己和儿子
            if(val[f[u]] >= val[u] && val[u] >= val[son[u][0]] && val[u] >= val[son[u][1]]) return Find(u);//树的形态没有变
            val[u] = get(u);
            return unite(del(u), u);//先删再合并
        }
    
        int build() {
            for(int i = q.size(), j, k; i > 1; -- i) {
                j = q.front(), q.pop();
                k = q.front(), q.pop();
                q.push(unite(j, k));
            }
            return q.front();//build 可以感性理解:尽量将点数差不多的合并在一起,可以使树长得没那么瘦。。。
        }
    }T1, T2;
    
    int main() {
        int n = read(), x, y, a, b, root, tot = 0; char op[10];
        for(int i = 1; i <= n; ++ i) q.push(i), T1.val[i] = T2.val[i] = read();
        root = T2.build();//初始每个点都是一棵树
        int q = read();
        while(q --) {
            scanf("%s", op);
            if(op[0] == 'U') {
                x = T1.Find(read()), y = T1.Find(read());
                if(x == y) continue;
                root = T2.del(T1.unite(x, y) == x ? y : x);//合并两个点时,其在树2就必须少一个点
            }
            else if(op[0] == 'A' && op[1] == '1') {
                x = read(), y = read();
                a = T1.Find(x), b = T1.add(x, y);
                if(a ^ b) {//说明 x 成为连通块的最大值,我们就更改在树2的位置与权值
                    root = T2.del(a);
                    T2.val[b] = T1.val[b];
                    root = T2.unite(root, b);
                }
                else if(x == a) root = T2.add(x, y);
            }
            else if(op[0] == 'A' && op[1] == '2') {
                x = T1.Find(read()), y = read();
                T1.val[x] += y, T1.la[x] += y;
                root = T2.add(x, y);
            }
            else if(op[0] == 'A' && op[1] == '3') {
                tot += read();
            }
            else if(op[0] == 'F' && op[1] == '1') {
                printf("%d
    ", T1.get(read()) + tot);
            }
            else if(op[0] == 'F' && op[1] == '2') {
                printf("%d
    ", T1.val[T1.Find(read())] + tot);
            }
            else {
                printf("%d
    ", T2.val[root] + tot);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    LeetCode 515. 在每个树行中找最大值(Find Largest Value in Each Tree Row)
    LeetCode 114. 二叉树展开为链表(Flatten Binary Tree to Linked List)
    LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)
    LeetCode 1022. 从根到叶的二进制数之和(Sum of Root To Leaf Binary Numbers)
    LeetCode 897. 递增顺序查找树(Increasing Order Search Tree)
    LeetCode 617. 合并二叉树(Merge Two Binary Trees)
    LeetCode 206. 反转链表(Reverse Linked List) 16
    LeetCode 104. 二叉树的最大深度(Maximum Depth of Binary Tree)
    LeetCode 110. 平衡二叉树(Balanced Binary Tree) 15
    LeetCode 108. 将有序数组转换为二叉搜索树(Convert Sorted Array to Binary Search Tree) 14
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12639082.html
Copyright © 2011-2022 走看看