• 2020.07.18 牛客多校第三场


    G.Operating on a Graph

    题意:

    给定一个n个点m条边的图,对每个点i一开始颜色都为i。现在给出q次操作,每次给出一种颜色col,对任意颜色x,若存在一个x颜色的点与col颜色的点直接相连,就要把所有x颜色的点都变成col颜色。求最终每个点的颜色。

    思路:

    首先,很容易想到要利用并查集,但是如何处理变色操作使复杂度不爆炸就需要仔细想想(比赛的时候帮队友去想数论了,挺可惜的)。

    有一个重要的观察发现是,每个点对相邻点的更新操作最多只有一次,之后的操作中就不用再考虑这些点了。那么我们先把每种颜色的点存在一种数据结构中 ,如 (a_1)(a_2)(a_3) 。当对颜色1操作时,假设1颜色的点存在边连接到2、3颜色的点,我们就更新 (a_1 = a_2+a_3)(a_2 = empty)(a_3=empty) (2和3都变成了1),同时在并查集中更新 (pre[2] = pre[3] = 1)

    由于q<=8e5,我们每次的合并只能O(1)。题解中给出的做法是用链表操作,代码如下。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x3f3f3f3f
    #define LLINF 0x3f3f3f3f3f3f3f3f
    #define pii pair<int,int>
    #define vi vector<int>
    #define SZ(x) (int)x.size()
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    const int MAXN = 8e5 + 5;
    const int MAXM = 8e5 + 5;
    //前向星----------------------
    struct edge {
        int to, next, w;
    } e[MAXM * 2]; //双倍内存
    int cnt = 0;
    int head[MAXN];
    void add_edge(int u, int v, int w) { //从1开始存
        e[++cnt].to = v;
        e[cnt].next = head[u];
        e[cnt].w = w;
        head[u] = cnt;
    }
    int pre[MAXN];
    int find(int x) {
        while(x != pre[x])
            x = pre[x] = pre[pre[x]];
        return x;
    }
    struct node {
        node() {};
        node(int x) {
            val = x;
        };
        node* next = NULL;
        int val; // 点标号
    };
    node* First[MAXN];
    node* Last[MAXN];
    void init(int n) {
        for(int i = 0; i <= n; i++)
            pre[i] = i;
        cnt = 0;
        fill(head, head + n, 0);
        for(int i = 0; i <= n; i++) {
            First[i] = new node(i);
            Last[i] = First[i];
        }
    }
    int main() {
        int t;
        scanf("%d", &t);
        while(t--) {
            int n, m;
            scanf("%d%d", &n, &m);
            init(n);
            for(int i = 0; i < m; i++) {
                int u, v;
                scanf("%d%d", &u, &v);
                add_edge(u, v, 0);
                add_edge(v, u, 0);
            }
    
            int q;
            scanf("%d", &q);
            while(q--) {
                int x;
                scanf("%d", &x);
                node* fir = NULL;
                node* las = NULL;
                for(node* i = First[x]; i != NULL; i = i->next) {
                    int u = i->val;
                    for(int j = head[u]; j; j = e[j].next) {
                        int v = e[j].to;
                        int col = find(v);
                        if(col != x && First[col] != NULL) {
                            if(fir == NULL) {
                                fir = First[col];
                                las = Last[col];
                            } else {
                                las->next = First[col];
                                las = Last[col];
                            }
                            pre[col] = x;
                            First[col] = Last[col] = NULL;
                        }
                    }
                }
                First[x] = fir;
                Last[x] = las;
            }
            for(int i = 0; i < n; i++)
                printf("%d ", find(i));
            printf("
    ");
        }
    }
    

    但是鉴于链表本来就很少写,生疏的话会出现指针乱飘debug半天的情况(指我),因此并不推荐(看了看大哥们好像也没人用链表写)。我又想了一下,发现用树也可以做到O(1)合并,乱打一发就秒了,代码如下。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x3f3f3f3f
    #define LLINF 0x3f3f3f3f3f3f3f3f
    #define pii pair<int,int>
    #define vi vector<int>
    #define SZ(x) (int)x.size()
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    const int MAXN = 8e5 + 5;
    const int MAXM = 8e5 + 5;
    //前向星----------------------
    struct edge {
        int to, next, w;
    } e[MAXM * 2]; //双倍内存
    int cnt = 0;
    int head[MAXN];
    void add_edge(int u, int v, int w) { //从1开始存
        e[++cnt].to = v;
        e[cnt].next = head[u];
        e[cnt].w = w;
        head[u] = cnt;
    }
    //并查集-----------------------
    int pre[MAXN];
    int find(int x) {
        while(x != pre[x])
            x = pre[x] = pre[pre[x]];
        return x;
    }
    //------------------------------
    vi ee[MAXN];
    int root[MAXN];
    void dfs(int u, int x) {
        for(int j = head[u]; j; j = e[j].next) {
            int w = e[j].to;
            int col = find(w);
            if(col != x) {
                if(root[x] == -1)
                    root[x] = root[col];
                else
                    ee[root[x]].pb(root[col]);
                root[col] = -1;
                pre[col] = x;
            }
        }
        for(int i = 0; i < SZ(ee[u]); i++) {
            int v = ee[u][i];
            dfs(v, x);
        }
    }
    void init(int n) {
        for(int i = 0; i <= n; i++)
            pre[i] = i;
        cnt = 0;
        fill(head, head + n, 0);
        for(int i = 0; i < n; i++) {
            ee[i].clear();
            root[i] = i;
        }
    }
    int main() {
        int t;
        scanf("%d", &t);
        while(t--) {
            int n, m;
            scanf("%d%d", &n, &m);
            init(n);
            for(int i = 0; i < m; i++) {
                int u, v;
                scanf("%d%d", &u, &v);
                add_edge(u, v, 0);
                add_edge(v, u, 0);
            }
            int q;
            scanf("%d", &q);
            while(q--) {
                int x;
                scanf("%d", &x);
                int rt = root[x];
                root[x] = -1;
                dfs(rt, x);
            }
            for(int i = 0; i < n; i++)
                printf("%d ", find(i));
            printf("
    ");
        }
    }
    
  • 相关阅读:
    IIS6.0服务器架站无法访问解决方案总结
    DNN中做支持多语言的模块
    在dotnetnuke中创建 parent portal
    DNN,Rainbow资源
    2005年岁末,各种主流CMS系统的比较和汇总
    在DNN中获取所有模块信息
    学习dnn的新资源,sooooooooo great!!
    DNN的电子商务模块
    DNN学习笔记
    也学ASP.NET 2.0 AJAX 之二:使用Timer控件
  • 原文地址:https://www.cnblogs.com/Hartley/p/13353283.html
Copyright © 2020-2023  润新知