点双连通分量学习笔记 大佬们的博客 Some Links

点双连通分量:

点双(点双连通分量)基于无向图,一个分量满足为点双当且仅当任意两个点之间都可找到两条点不重复的路径,可以理解为若干个有边相交的环。
有一个性质为点双内任意两点的点不重复路径都在点双内。

缩点双:

用tarjan求割顶的办法来求点双,即在求割顶的过程中用一个栈来记录下经过的点,当找到一个点为割顶的时候就弹栈,直到把它目前指向的那个结点给弹完。有一点需要注意的是每一个割顶可能存在于多个点双内,这就是为什么之前只弹到它之前指向的结点,应为它自己可能还在很多个点双内,最后单独记录一下目前的割顶就好了。

圆方树:

圆方树是在缩点双的基础下形成的一种算法,即每一个点双单独开一个方点,方点作为中心和之前的每一个点双内的点(圆点)连边。这里比较特殊的是两个点一条边也算是一个点双,中间用一个方点来连接,对最后的树上处理并没有什么影响。
这样一条图上的路径就变成了树上的路径,经过方点就表示经过了这个方点所表示的点双。
例题:求一个图中1号点到每一个点的点不重复路径中的点权最小值。
做法:直接建立圆方树后跑一遍dfs即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=2e5+10;
const int maxm=2e5+10;
const int maxq=1e6+10;
const int inf=0x7f7f7f7f;
int n,m,q,query[maxq],w[maxn],tot,ans[maxn];
vector<int>to1[maxn],to2[maxn];
int low[maxn],dfn[maxn],cnt_dfn;
stack<int>s;

void tarjan(int u,int f){
    dfn[u]=low[u]=++cnt_dfn; s.push(u);
    int siz=to1[u].size()-1;
    REP(i,0,siz){
        int v=to1[u][i];
        if(v==f)continue;
        if(!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                ++tot;
                int Minval=inf;
                while(s.size()){
                    int node=s.top();
                    to2[tot+n].push_back(node);
                    to2[node].push_back(tot+n);
                    Minval=min(Minval,w[node]);
                    s.pop(); if(node==v)break;
                }
                to2[tot+n].push_back(u);
                to2[u].push_back(tot+n);
                Minval=min(Minval,w[u]);
                w[n+tot]=Minval;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}

void solve(int u,int f){
    ans[u]=min(ans[f],w[u]);
    int siz=to2[u].size()-1;
    REP(i,0,siz){
        int v=to2[u][i];
        if(v==f)continue;
        solve(v,u);
    }
}

void init(){
    read(n); read(m); read(q);
    int u,v;
    REP(i,1,m){
        read(u); read(v);
        to1[-u].push_back(-v);
        to1[-v].push_back(-u);
    }
    REP(i,1,n)read(w[i]);
    REP(i,1,q)read(query[i]);
    tarjan(1,0);
    REP(i,0,n+tot)ans[i]=inf;
}

int main(){
    File();
    init();
    solve(1,0);
    REP(i,1,q)printf("%d\n",ans[query[i]]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81386050