• CF652E Pursuit For Aritifacts


    题目传送门

    这是一道很好的练习强联通的题目。 首先,从题中可以看到,题目的要求就是要我们求出从起点到终点是否可以经过flag = 1 的边。 由于是无向图,且要求很多,直接暴力dfs会很凌乱。

    那么,我们就想到一个思路:能不能尽量把这张图缩小,标记转为点,最好成为一条一条链呢?

    tarjan的缩点!!

    没错,对于一个环,可以想到,只要这个环中有一条边flag = 1,那么所有的点我们都可以通过falg = 1的边到达(因为这是环)。所以,不妨进行tarjan缩点,只要这个缩点中有一条边falg = 1,我们就把这个缩点打上tag。

    再一想,经过缩点之后,原来十分凌乱的图就变成了一棵树。到达终点的路线也就只有固定一条了。这里我选择dfs。

    思路大体就是这样,总时间复杂度O(M + N)

    话不多说,具体细节操作标记在代码里面了。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 500010
    #define isdigit(c) ((c)>='0'&&(c)<='9')
    
    inline int read(){
        int x = 0,s = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-') s = -1;
            c = getchar();
        } 
        while(isdigit(c)){
            x = (x << 1) + (x << 3) + (c ^ '0');
            c = getchar();
        }
        return x * s;
    }
    
    int n, m;
    struct node{
        int u, v, flag;
        int next;
    } t[N << 1]; 
    int f[N];//日常邻接表 
    
    int s, ht;//起点, 终点 
    int dfn[N], low[N], scc[N]; //scc即为缩点后每个缩点的编号 
    stack <int> stac;//缩点用的
    bool vis[N];//一个点是否被经过 
    
    int bian = 0;
    void add(int u, int v, int flag){
        bian++;
        t[bian].u = u;
        t[bian].v = v;
        t[bian].flag = flag;
        t[bian].next = f[u];
        f[u] = bian;
        return ;
    }
    
    int cnt = 0, cac;//cac为强联通的数量 
    void tarjan(int now, int fa){//无向图的tarjan强联通 板子 
        dfn[now] = low[now] = ++cnt;
        vis[now] = 1;
        stac.push(now);
        for(int i = f[now]; i;i = t[i].next){
            int v = t[i].v;
            if(v != fa){
                if(!dfn[v]){
                    tarjan(v, now);
                    low[now] = min(low[now], low[v]);
                }
                else if(vis[v])low[now] = min(low[now], low[v]);
            }
        }
        if(dfn[now] == low[now]){
            int cur;
            cac++;
            do{
                cur = stac.top();
                stac.pop();
                scc[cur] = cac;
                vis[cur] = 0;
            }while(cur != now);
        }
        return ;
    }
    
    bool tong[N]; //tong为每个缩点被打上的标记,即上文所说的,是否包含flag = 1的边 
    void dfs(int now, bool flag){
        if(tong[now])flag = 1;//这个缩点标记为1的话,记下来 
        if(now == scc[ht]){
            if(flag)puts("YES");//搜到终点,没什么好说的 
            else puts("NO");
            return ;
        }
        for(int i = f[now]; i;i = t[i].next){
            int v = t[i].v, u = t[i].u;
            if(!vis[v]){
                vis[v] = 1;
                dfs(v, flag | t[i].flag);//这里要注意不要漏掉了缩点与缩点之间的边的 flag 
            }   
        }
        return ;
    }
    
    int main(){
        n = read(), m = read();
        for(int i = 1;i <= m; i++){
            int x = read(), y = read(), tag = read();
            add(x, y, tag);add(y, x, tag);
        }
        s = read() , ht = read();
        tarjan(1, 0);
        for(int i = 1;i <= bian; i += 2){
            if(scc[t[i].u] == scc[t[i].v] && t[i].flag){
                tong[scc[t[i].u]] = 1;//为强联通分量中的边,且flag = 1 
            }
        }
        memset(f, 0, sizeof(f));//重复利用 
        bian = 0;
        memset(vis, 0, sizeof(vis));
        for(int i = 1;i <= m << 1; i++){
            int u = t[i].u, v = t[i].v;
            if(scc[u] != scc[v]){
                add(scc[u], scc[v], t[i].flag);//不同缩点之间的连边,需要保留。flag不能改 
            }
        }
        vis[scc[s]] = 1;
        dfs(scc[s], 0);//dfs的都是缩点,这点不要忘了 
        return 0;
    }
  • 相关阅读:
    RecycleView实现多布局可展开列表
    ubuntu在anaconda2下安装anaconda3环境 && 在Pycharm中配置Python3
    向量的叉乘
    角动量与角动量守恒
    语言学 —— 中文的构词与规律
    动力系统 —— 液压与气压
    Keras GRU 文字识别
    造物的科学 —— 保温瓶、衣服防盗器
    循环神经网络RNN模型和长短时记忆系统LSTM
    ResNet网络结构
  • 原文地址:https://www.cnblogs.com/wondering-world/p/12634460.html
Copyright © 2020-2023  润新知