zoukankan      html  css  js  c++  java
  • P2783 有机化学之神偶尔会做作弊 题解

    Description

    Luogu传送门

    Solution

    \(Tarjan\) 边双缩点 + 树剖求 \(lca\)

    其实就是个板子但是我卡了一晚上QwQ

    由于是无向图,所以先跑一遍 \(Tarjan\) 找到所有的,然后 dfs 遍历整张图,找到所有的边双连通分量,每个边双指定一个新编号,然后再在边双之间连边建新图。

    建完图之后利用树剖(或者其他什么都行)求个 \(lca\),再树上差分一下即可。

    坑点:

    • 此题有重边,重复的边只能加一遍,我用的 hash + map 实现的吗,把边 hash 一下然后map标记一下。
    • 建新图的时候一定要用边双的编号建。
    • 查询 \(lca\) 的时候也是边双的编号啊啊啊啊啊啊啊(因为这个卡了一晚上,感谢 LawrenceSivan 巨佬帮我看出问题)

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    namespace IO{
        inline int read(){
            int x = 0;
            char ch = getchar();
            while(!isdigit(ch)) ch = getchar();
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x;
        }
    
        template <typename T> inline void write(T x){
            if(x > 1) write(x / 2);
            putchar(x % 2 + '0');
        }
    }
    using namespace IO;
    
    const int N = 1e5 + 1;
    const int M = 5e5 + 1;
    unordered_map <int, bool> mp;
    int n, m, q;
    struct node{
        int u, v, nxt;
    }edge[M << 1];
    int head[N], tot = 1;
    int bri[N << 1];
    
    inline void add(int x, int y){
        edge[++tot] = (node){x, y, head[x]};
        head[x] = tot;
    }
    
    namespace Tarjan{
        int dfn[N], low[N], tim;
        int stk[N], top;
        bool vis[N];
        int cnt, scc[N];
        queue <int> que;
    
        inline void tarjan(int x, int e){
            dfn[x] = low[x] = ++tim;
            for(int i = head[x]; i; i = edge[i].nxt){
                int y = edge[i].v;
                if(e == (i ^ 1)) continue;
                if(!dfn[y]){
                    tarjan(y, i), low[x] = min(low[x], low[y]);
                    if(low[y] > dfn[x]) bri[i] = bri[i ^ 1] = 1;
                }else low[x] = min(low[x], dfn[y]);
            }
        }
    
        inline void dfs(int x){
            vis[x] = 1;
            que.push(x);
            for(int i = head[x]; i; i = edge[i].nxt){
                int y = edge[i].v;
                if(!vis[y] && !bri[i]) dfs(y);
            }
        }
    }
    using namespace Tarjan;
    
    namespace Cut_Tree{
        int siz[N], son[N], fa[N], dep[N];
        int top[N], dfn[N], tim;
        vector <int> g[N];
    
        inline void dfs1(int x, int p){
            fa[x] = p, dep[x] = dep[p] + 1, siz[x] = 1;
            for(auto y : g[x]){
                if(y == p) continue;
                dfs1(y, x);
                siz[x] += siz[y];
                if(siz[y] > siz[son[x]]) son[x] = y;
            }
        }
    
        inline void dfs2(int x, int topfa){
            dfn[x] = ++tim, top[x] = topfa;
            if(!son[x]) return;
            dfs2(son[x], topfa);
            for(auto y : g[x]){
                if(y == fa[x] || y == son[x]) continue;
                dfs2(y, y);
            }
        }
    
        inline int lca(int x, int y){
            while(top[x] != top[y]){
                if(dep[top[x]] < dep[top[y]]) swap(x, y);
                x = fa[top[x]];
            }
            return dep[x] < dep[y] ? x : y;
        }
    }
    using namespace Cut_Tree;
    
    int main(){
        n = read(), m = read();
        for(int i = 1; i <= m; ++i){
            int u = read(), v = read();
            if(u > v) swap(u, v);
            if(!mp[u * n + v]) add(u, v), add(v, u), mp[u * n + v] = 1;//判重边,不知道这样写对不对 QwQ
        }
        for(int i = 1; i <= n; ++i)//找桥
            if(!Tarjan::dfn[i]) tarjan(i, 1);
        for(int i = 1; i <= n; ++i){//找到所有联通块
            if(!vis[i]){
                dfs(i), cnt++;
                while(!que.empty())
                    scc[que.front()] = cnt, que.pop();
            }
        }
        mp.clear();
        for(int i = 2; i <= tot; ++i){//建新树
            int u = scc[edge[i].u], v = scc[edge[i].v];
            if(u > v) swap(u, v);
            if(u != v && !mp[u * n + v]){
                g[u].push_back(v), g[v].push_back(u);
                mp[u * n + v] = 1;
            }
        }
        dfs1(1, 0), dfs2(1, 1);
        q = read();
        while(q--){
            int x = scc[read()], y = scc[read()];
            write(dep[x] + dep[y] - (dep[lca(x, y)] << 1) + 1), puts("");
        }
        return 0;
    }
    

    \[\_EOF\_ \]

  • 相关阅读:
    c语言 9-3
    c语言 9-12
    c语言 9-11
    c语言 9-10
    c语言中实现字符串大小写的转换
    tyvj1106 登山
    tyvj1125 JR's chop
    tyvj1148 小船弯弯
    tyvj1087 sumsets
    tyvj1086 Elevator
  • 原文地址:https://www.cnblogs.com/xixike/p/15565406.html
Copyright © 2011-2022 走看看