• 【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;
    }
    
  • 相关阅读:
    Android应用开发SharedPreferences存储数据的使用方法
    Android ListView不响应OnItemClickListener解决办法
    Java 毫秒转换为日期类型、日期转换为毫秒
    关于android软键盘enter键的替换与事件监听
    如何使用adb命令查看android中的数据库
    android坐标
    getHitRect获取点击控件的位置
    UTC的相互转换(java)
    深入理解Android的startservice和bindservice
    TS格式解析
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12639082.html
Copyright © 2020-2023  润新知