• (可并堆)p3377 左偏树 + P1456 Monkey King


    左偏树是一种支持log(n)合并的堆式数据结构。

    定义:

    外节点:不同时拥有左右儿子的节点

    x的距离(dist):x到达子树中(包括自己)最近的外节点的距离,特别定义空节点的dist为-1

    基本性质和结论:

    除了满足堆的每个节点都 大于 或者 小于 左右儿子的性质,还满足(大概不全)

    1、左偏性:对于每个节点, dist[ls]>=dist[rs], ls=leftson rs=rightson

    2、dist[x] = dist[rs] + 1

    3、n个节点的左偏树的dist[root] = log_2(n)

    基本操作merge(注:以下均为小根堆)

    总的来说,merge总节点数不变,但需要同时维护堆的性质和左偏性。可以通过不断递归以权值为标准把堆分成很多单元,再向上合并,最后再维护左偏性。

    流程:

    1、对于两个左偏树的根节点x, y,v[x] < v[y]

    2、用以rs[x]为根的左偏树去和以y为根的左偏树merge(向下递归相当于把两个左偏树拆成很多片),再让rs[x] = 合并后的结果

    3、重复以上12操作,如果!x||!y为空节点直接返回 x+y(每次都是y不变,x = rs[x],到最后“rs[x] = merge(rs[x], y);”中rs[x]是0、y不是0,然后把y放在rs[x]上)

    4、维护左偏性:如果dist[rs[x]] > dist[ls[x]],则swap(rs[x], ls[x]);

    int v[maxn], ls[maxn], rs[maxn], dist[maxn];
    
    int merge(int x, int y)
    {
        if (!x || !y)return x + y;
        if (v[x] > v[y])swap(x, y);//ensure v[x]<v[y]
        rs[x] = merge(rs[x], y);
        if (dist[rs[x]] > dist[ls[x]])swap(rs[x], ls[x]);
        dist[x] = dist[rs[x]] + 1;
        return x;//x和y之前可能换过
    }
    

    对于这题

    插入:把大小为1的左偏树合并上去

    合并:字面意思

    删除:把根删除,合并左右两个子树。(由于这题用了并查集,根节点会作为路径压缩的终点,所以删除以后要fa[x] = merge(ls[x],rs[x]))

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    using namespace std;
    double pi = acos(-1);
    
    const double eps = 1e-6;
    const int maxn = 1e5 + 10;
    const ll mod = 1e9 + 7;
    const int inf = 1e11;
    
    int v[maxn], ls[maxn], rs[maxn], dist[maxn];
    
    int merge(int x, int y)
    {
        if (!x || !y)return x + y;
        if (v[x] > v[y])swap(x, y);//ensure v[x]<v[y]
        rs[x] = merge(rs[x], y);
        if (dist[ls[x]] < dist[rs[x]])swap(rs[x], ls[x]);
        dist[x] = dist[rs[x]] + 1;
        return x;
    }
    
    int fa[maxn];
    
    int anc(int x) { return x == fa[x] ? x : fa[x] = anc(fa[x]); }
    
    int main()
    {
        fastio;
        memset(v, -1, sizeof(v));
        dist[0]=-1;
        int n, q;
        cin >> n >> q;
        for (int i = 1; i <= n; i++)cin >> v[i], fa[i] = i;
        while (q--)
        {
            int o;
            cin >> o;
            if (o == 1)
            {
                int x, y;
                cin >> x >> y;
                if (v[x] == -1 || v[y] == -1)continue;
                x = anc(x), y = anc(y);
                if (x != y)
                    fa[x] = fa[y] = merge(x, y);
            }
            else
            {
                int x;
                cin >> x;
                if (v[x] == -1)
                {
                    cout << -1 << endl;
                    continue;
                }
                x = anc(x);
                cout << v[x] << endl;
                v[x] = -1;
                fa[x] = fa[ls[x]] = fa[rs[x]] = merge(ls[x], rs[x]);
                //ls[x] = rs[x] = dist[x] = 0;
            }
        }
        return 0;
    }
    

    P1456 Monkey King

    模版题,把顶部除2之后就不能保证堆的性质,所以要把它先删除,合并左右儿子,再单独把它合并进去(记得初始化左右儿子)

    #include<bits/stdc++.h>
    #define ll long long
    #define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    using namespace std;
    
    const int maxn = 1e5 + 10;
    
    int v[maxn], ls[maxn], rs[maxn], dist[maxn];
    
    int merge(int x, int y)
    {
        if (!x || !y)return x + y;
        if (v[x] < v[y])swap(x, y);
        rs[x] = merge(rs[x], y);
        if (dist[ls[x]] < dist[rs[x]])swap(rs[x], ls[x]);
        dist[x] = dist[rs[x]] + 1;
        return x;
    }
    
    int fa[maxn];
    
    int anc(int x) { return x == fa[x] ? x : fa[x] = anc(fa[x]); }
    
    int Make_merge(int x, int y)
    {
        fa[x] = fa[y] = merge(x, y);
        return fa[x];
    }
    
    int main()
    {
        fastio;
        int n;
        while (cin >> n)
        {
            dist[0] = -1;
            for (int i = 1; i <= n; i++)
                cin >> v[i], fa[i] = i, ls[i] = rs[i] = dist[i] = 0;
            int q;
            cin >> q;
            int a, b;
            while (q--)
            {
                int x, y;
                cin >> x >> y;
                x = anc(x), y = anc(y);
                if (x == y)
                {
                    cout << -1 << endl;
                    continue;
                }
                a = Make_merge(ls[x], rs[x]), b = Make_merge(ls[y], rs[y]);
                v[x] /= 2;
                v[y] /= 2;
                rs[x] = ls[x] = rs[y] = ls[y] = 0;
                a = Make_merge(a, x);
                b = Make_merge(b, y);
                cout << v[Make_merge(a, b)] << "
    ";
            }
        }
        return 0;
    }
    
  • 相关阅读:
    【转】C++多继承的细节
    【转】CVE-2010-4258 漏洞分析
    【转】cve-2013-2094 perf_event_open 漏洞分析
    android CVE 漏洞汇总
    ExecutorService中submit和execute的区别
    线程池之ThreadPoolExecutor使用
    postman接口自动化,环境变量的用法详解(附postman常用的方法)转
    件测试专家分享III GUI自动化测试相关
    Linux上运行Jmeter
    时间复杂度和空间复杂度计算
  • 原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/15190999.html
Copyright © 2020-2023  润新知