• 2020牛客暑期多校训练营(第三场)G Operating on a Graph


    思路:首先想到暴力做法,每次将 其他点并到节点 (a) 时,删掉 (a) 现有的连边并将新加入的 (group) 中的点连出去的所有边连到 (a) 上,并将的这些节点的父亲设为 a,

    ​ 每次都这么操作,是能保证可以得出正确结果的,但是一定会 (tle) ,维护父亲节点我们可以用并查集,但是将新加入的 (group) 中的点连出去的所有边连到 (a) 上相当于复制所有边,这样操作的复杂度会很高,其实我们可以不用复制所有边,如果你前向星见图掌握的好的话,我们其实可以发现,一个点的所有边可 以用一个序列表示出来,(head[u]) 就是这个序列的第一个点,我们再维护一下这个序列时什么时候结束的,每次要将新节点的边复制到节点 (a) 时,我们可以 将这些新节点的边序列连起来,具体怎么连看代码, 再让 (head[a]) 等于这个序列的起点,这样的复杂度就会低很多,粗略的算了一下,大概是 (o(2 imes m)) 的复杂度,因为每条边只会被遍历一次,加上建图的时候的反边,就是 (o(2 imes m)) , 至于父节点可以用并查集维护。

    ​ 详细请看以下代码

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <string>
    #include <string.h>
    #include <map>
    #include <iostream>
    using namespace std;
    const int maxn = 2e6 + 50;
    const int mod = 20090717;
    typedef long long LL;
    int INF = 1e9;
    typedef pair<int, int> pii;
    #define fi first
    #define se second
    
    int fa[maxn]; // 并查集
    int Find(int x){ 
        if(fa[x] == x){
            return x;
        }
        return fa[x] = Find(fa[x]);
    }
    
    void unit(int x, int y){
        x = Find(x), y = Find(y);
        if(x == y) return ;
        fa[y] = x;
    }
    struct Edge
    {
        int to, next;
    } edge[maxn * 2];
    int k, head[maxn];
    int sid[maxn], eid[maxn]; // 记录一个节点的起始边和终止边
    void add(int a, int b){
        edge[k].to = b;
        edge[k].next = head[a];
        if(eid[a] == -1) sid[a] = eid[a] = k;
        else sid[a] = k;
        head[a] = k++;
        
    }
    int vis[maxn];
    queue<int> que;
    int main(int argc, char const *argv[])
    {
        int t;
        scanf("%d", &t);
        while(t--){
            k = 0;
            int n, m;
            scanf("%d%d", &n, &m);
            for(int i = 0; i <= n; i++){
                head[i] = -1;
                vis[i] = 0, fa[i] = i;
                sid[i] = eid[i] = -1;
            }
            for(int i = 1; i <= m; i++){
                int a, b;
                scanf("%d%d", &a, &b);
                add(a, b);
                add(b, a);
            }
            int q;
            scanf("%d", &q);
            while(q--){
                int u;
                scanf("%d", &u);
                if(vis[u]) continue; 
                for(int i = head[u]; i != -1; i = edge[i].next){
                    int to = edge[i].to;
                    to = Find(to);
                    if(to == u) continue;
                    unit(u, to);
                    vis[to] = 1; // group[to] 为空,标记一下
                    que.push(to); // 存一下新加入的点
                }
                int fg = 1;
                head[u] = -1; // 这句要记得加上,因为如果已经没有点可以并到group上,那么head[u]不会更新,之后每次操作u都会遍历最后一次连在u上的点,会导致tle
                while(que.size()){
                    int to = que.front();
                    que.pop();
                    if(fg) {
                        fg = 0;
                        sid[u] = sid[to]; // 记录新的起点
                        head[u] = sid[to]; 
                    } else {
                        edge[eid[u]].next = sid[to]; // 将不同的两个点的边序列链接
                    } 
                    eid[u] = eid[to]; // 记录终点
                }
            }
    
            for(int i = 0; i < n; i++){
                Find(i);
                printf("%d ", fa[i]);
            }
            printf("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    1.4(java学习笔记) 面向对象内存分析
    1.3(java学习笔记)构造方法及重载
    1.2(java学习笔记)类与对象
    1.1(java学习笔记) 面向过程与面向对象
    STM32F0库函数初始化系列:GPIO配置
    STM32F4库函数初始化系列:PWM输出
    STM32F4库函数初始化系列:DMA串口接收
    STM32F4库函数初始化系列:三重ADC——DMA
    STM32F1库函数初始化系列:DMA—ADC采集
    STM32F4库函数初始化系列:串口DMA接收
  • 原文地址:https://www.cnblogs.com/PCCCCC/p/13336827.html
Copyright © 2020-2023  润新知