zoukankan      html  css  js  c++  java
  • P5631 最小mex生成树 分治 并查集

    P5631 最小mex生成树

    题目链接

    ​ 分治+并查集。

    ​ 这道题是1004考试题,当时在考场想到50分做法,就是从小到大枚举没有出现在生成树中最小的边,然后除了这种边权的边其它边都可以建生成树,如果建不成就说明找到了。

    ​ 其实正解是优化一下这个过程:我们对边权进行分治,最后就会剩下一条边不加入生成树,然后我们判断是否合法,也就是看是否只有一个联通块,如果是直接输出。如果不是肯定要回溯,我们需要把之前连在一起的断开,也就是还原并查集。我们用一个栈维护并查集的操作,弹栈的时候倒叙进行相反的操作就好了。这样避免了每次都建一次生成树,复杂度(O(m longn longw))

    #include <bits/stdc++.h>
    
    #define mid ((l + r) >> 1)
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1e6 + 5, inf = 1e5;
    int n, m, num, top, flag;
    int fa[N], sta[N << 2], dep[N];
    vector <pair<int, int> > e[N];
    
    int find(int x) { return fa[x] == x ? x : find(fa[x]); }
    
    void unionn(int x, int y) {
        int tx = find(x), ty = find(y);
        if(tx == ty) return ;
        if(dep[tx] > dep[ty]) swap(tx, ty);
        if(dep[tx] == dep[ty]) {
            dep[ty] ++; sta[++ top] = -ty;
        }
        sta[++ top] = tx; num --; fa[tx] = ty;
    }
    
    void split(int x) {
        while(top > x) 
            if(sta[top] < 0) dep[-sta[top]] --, top --;
            else fa[sta[top]] = sta[top], top --, num ++;
    }
    
    void solve(int l, int r) {
        // cout << l << " " << r << " " << num << endl;
        // for(int i = 1;i <= 1e8; i++);
        if(flag) return ;
        if(num == 1) { printf("%d", l); flag = 1; return ; }
        if(l == r) return ;
        int tmp = top;
        for(int i = mid + 1;i <= r; i++)
            for(int j = 0;j < (int)e[i].size(); j++)
                unionn(e[i][j].first, e[i][j].second);
        solve(l, mid); split(tmp);
        for(int i = l;i <= mid; i++) 
            for(int j = 0;j < (int)e[i].size(); j++) 
                unionn(e[i][j].first, e[i][j].second);
        solve(mid + 1, r); split(tmp);
    }
    
    int main() {
    
        n = read(); m = read(); num = n;
        for(int i = 1, a, b, c;i <= m; i++) a = read(), b = read(), c = read(), e[c].push_back(make_pair(a, b));
        for(int i = 1;i <= n; i++) fa[i] = i;
        solve(0, inf);
    
        return 0;
    }
    
  • 相关阅读:
    Linux:Day45(下)
    Linux:Day45(上)
    Linux:Day44(下)
    彻底理解JDK异步
    Ribbon是什么?
    RocketMQ学习笔记(一)eclipse版的quickstart
    线性表的遍历
    八皇后问题
    汉诺塔
    servlet的url-pattern规则
  • 原文地址:https://www.cnblogs.com/czhui666/p/13789888.html
Copyright © 2011-2022 走看看