zoukankan      html  css  js  c++  java
  • Tarjan的学习

    /*推导https://blog.csdn.net/qq_34374664/article/details/77488976

    dfn[]为这个点搜索的次序编号(时间戳)
    low[]为每个点在这颗树中最小的子树的根
    每次找到一个新点,这个点low[]=dfn[]

    【强连通分量】 

    割点模板:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<set>
    using namespace std;
    #define REG register
    #define REP(i,x,y) for(register int i=x;i<=y;i++)
    #define UP(i,x,y) for(register int i=x;i>=y;i--)
    #define IN inline
    #define inf 0x3f3f3f3f
    
    const int maxn=100005;
    int h[maxn],dfn[maxn],low[maxn],fa[maxn],n,m,tot=0,num=0,sum=0,root;
    bool vis[maxn];
    struct node{
        int u,v;
    }e[maxn<<1];
    
    IN void add(int u,int v){
        e[++tot].u=h[u],e[tot].v=v,h[u]=tot;
    }
    
    IN void TARJAN(int x){
        dfn[x]=low[x]=++num;
        int flag=0;
        for(REG int i=h[x];i;i=e[i].u){
            int v=e[i].v;
            if(!dfn[v]){
                TARJAN(v);
                low[x]=min(low[x],low[v]);
                if(low[v]>=dfn[x]){
                    flag++;
                    if(x!=root || flag>1) vis[x]=true;
                }
            }
            else low[x]=min(low[x],dfn[v]);
        }
    }
    
    int main(){
        scanf("%d %d",&n,&m);
        REP(i,1,m){
            int x,y;
            scanf("%d %d",&x,&y);
            if(x==y) continue;
            add(x,y);add(y,x);
        }
        REP(i,1,n)
            if(!dfn[i]) root=i,TARJAN(i);
        REP(i,1,n)
            if(vis[i]) sum++;
        printf("%d
    ",sum);
        REP(i,1,n)
            if(vis[i]) printf("%d ",i);
        
        return 0;
    }
    luogu3388

    缩点模板:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<set>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<cstdlib>
    using namespace std;
    #define REG register
    #define IN inline
    #define REP(i,x,y) for(REG int i=x;i<=y;i++)
    #define inf 0x3f3f3f3f 
    
    const int maxn=100005;
    int n,m,u,v,top,time=0,ctot,ans,tot=0;
    bool vis[maxn];
    int sta[maxn]; 
    int clor[maxn];//染色(联通分量编号) 
    int num[maxn];//联通分量点权和 
    int dfn[maxn],low[maxn],x[maxn],y[maxn],a[maxn];
    int dis[maxn],first[maxn];
    struct n{
        int to,nt;    
    }node[maxn];
    
    IN void add(int u,int v){
        node[++tot].to=v,node[tot].nt=first[u],first[u]=tot;
    }
    
    IN void tarjan(int x){
        dfn[x]=low[x]=++time;
        sta[++top]=x;
        vis[x]=1;
        for(int i=first[x];i;i=node[i].nt){
            int j=node[i].to;
            if(vis[j])low[x]=min(low[x],dfn[j]);
            else if(!dfn[j])tarjan(j),low[x]=min(low[j],low[x]);
        }
        if(low[x]==dfn[x]){
            ++ctot;
            vis[x]=0;
            while(sta[top+1]!=x){
                clor[sta[top]]=ctot;//染色 
                num[ctot]+=a[sta[top]];//同一连通分量点权和 
                vis[sta[top]]=0;
                top--;
            }
        }
    }
    
    IN void spfa(int x){
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        queue<int> q;
        dis[x]=num[x];
        vis[x]=1;
        q.push(x);
        while(!q.empty()){
            int j=q.front();
            vis[j]=0;
            q.pop();
            for(int i=first[j];i;i=node[i].nt){
                int t=node[i].to;
                if(dis[t]<dis[j]+num[t]){
                    dis[t]=dis[j]+num[t];
                    if(!vis[t]){
                        vis[t]=1;
                        q.push(t);
                    }
                }
            }
        }
        REP(i,1,ctot) ans=max(ans,dis[i]);
    }
    
    int main(){
        scanf("%d %d",&n,&m);
        REP(i,1,n)scanf("%d",&a[i]);
        REP(i,1,m){
            scanf("%d %d",&u,&v);
            add(u,v);
            //x[i]=u;y[i]=v; 
        } 
        REP(i,1,n)
            if(!dfn[i]) tarjan(i);
        memset(first,0,sizeof(first));
        memset(node,0,sizeof(node));
        REP(i,1,m)
            if(clor[u]!=clor[v]) add(clor[u],clor[v]);
        REP(i,1,ctot) spfa(i);
        printf("%d",ans);
        
        return 0;
    }
    luogu3387

    无向图的双连通分量:一张无向连通图不存在割点。

    分为 点双连通分量(v-DCC) 和 边双连通分量(e-DCC)。

    图中任意两点都同时包含在至少一个简单环中。

    e-DCC模板:

    void tarjan(int x,int fa){
        dfn[x]=low[x]=++dfsnum;
        for(int i=first[x];i;i=e[i].from)if((i^1)!=fa){
            if(!dfn[e[i].v]){
                tarjan(e[i].v,i);
                low[x]=min(low[x],low[e[i].v]);
                if(low[e[i].v]>dfn[x])iscut[i]=iscut[i^1]=1;
            }else low[x]=min(low[x],dfn[e[i].v]);
        }
    }
    
    void dfs(int x){
        col[x]=colnum;
        for(int i=first[x];i;i=e[i].from)if(!col[e[i].v]&&!iscut[i])dfs(e[i].v);
    }
    View Code

    简单环就是双连通分量,所以双连通缩点后就是无向无环连通图。

    有向强连通分量(SCC):

    将图变为DAG

    void tarjan(int x)
    {
        dfn[x]=low[x]=++mark;
        s[++top]=x;lack[x]=top;
        for(int i=first[x];i;i=e[i].from)
         {
             int y=e[i].v;
             if(!dfn[y])
              {
                  tarjan(y);
                  low[x]=min(low[x],low[y]);
              }
             else if(!col[y])low[x]=min(low[x],dfn[y]);
         }
        if(dfn[x]==low[x])
         {
             color++;
             for(int i=lack[x];i<=top;i++)col[s[i]]=color;
             num[color]=top-lack[x]+1;
             top=lack[x]-1;
         }
    }
    for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
    View Code

    【最近公共祖先】

    void add(int u,int v){
        e[k].v=v;
        e[k].next=head[u];
        head[u]=k++;
    }
    
    void dfs(int u,int fa){
        d[u]=d[fa]+1;
        p[u][0]=fa;
        for(int i=1;(1<<i)<=d[u];i++)
            p[u][i]=p[p[u][i-1]][i-1];
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(v!=fa)
                dfs(v,u);
        }
    }
    
    int lca(int a,int b){
        if(d[a]>d[b])
            swap(a,b);
        for(int i=20;i>=0;i--)
            if(d[a]<=d[b]-(1<<i))
                b=p[b][i];
        if(a==b)
            return a;
        for(int i=20;i>=0;i--){
            if(p[a][i]==p[b][i])
                continue;
            else
                a=p[a][i],b=p[b][i];
        }
        return p[a][0];
    }
    
    dfs(s,0);
    倍增+LCA
  • 相关阅读:
    python爬虫实例--爬取拉勾网
    面试时,如何回答你还有什么想要了解的?
    深入理解三次握手四次挥手以及使用scapy实现ddos雏形
    解决socket粘包的两种low版模式 os.popen()和struct模块
    浅谈osi模型 三次握手 四次挥手 ddos攻击原理
    渲染相关书籍
    unity 场景编辑器物体加图标
    音乐模拟器
    3d服装制作软件
    uv投影插值
  • 原文地址:https://www.cnblogs.com/EvfX/p/8595572.html
Copyright © 2011-2022 走看看