首页 > 其他分享 >Tarjan

Tarjan

时间:2023-08-08 18:46:53浏览次数:36  
标签:Tarjan idx vis int ++ dfn low

我写这个随笔是让我更加理解算法,防止以后出错,因为我今天调Tarjan调了3个多小时,后来还是在大佬的帮忙下调出来了,问题不少

先来看错误的(40pts):

 

//缩点
//https://www.luogu.com.cn/problem/P3387
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,M=1e4+10;
int n,m,a[N],res,dist[N],id[N],p[N];
int e[N],ne[N],h[N],w[N],idx;
int dfn[N],low[N],_size[N],_stack[N];
int times,top,scc_num;
bool vis[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
    low[u]=dfn[u]=++times;
    _stack[++top]=u,vis[u]=true;
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(!dfn[j]) tarjan(j),low[u]=min(low[u],low[j]);
        else if(vis[j]) low[u]=min(low[u],dfn[j]);
    }
    if(low[u]==dfn[u]){
        int y;
        ++scc_num;
        do{
            y=_stack[top--];
            vis[y]=false,id[y]=scc_num;
            if(u==y) break;
            a[u]+=a[y];
        }while(y!=u);
    }
}
void topsort()
{
    queue<int>que;
    for(int i=1;i<=n;i++){
        if(id[i]==i&&!p[i]) que.push(i),dist[i]=a[i];
    }
    while(!que.empty()){
        int now=que.front(); que.pop();
        for(int i=h[now];~i;i=ne[i]){
            int j=e[i];
            p[j]--,dist[j]=max(dist[j],dist[now]+a[j]);
            if(p[j]==0) que.push(j);
        }
    }
}
signed main()
{
    cin>>n>>m;
    memset(h, -1, sizeof h);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        add(x,y);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=N;i++) h[i]=-1,ne[i]=e[i]=w[i]=0;
    for(int i=1;i<=n;i++)
        for(int j=h[i];~j;j=ne[j]){
            int k=e[j];
            int x=id[j],y=id[k];
            if(x!=y) add(x,y),p[y]++;
        }
    topsort();
    for(int i=1;i<=n;i++) res=max(res,dist[i]);
    cout<<res<<endl;
    return 0;
}

 

修正之后:

//本题基本思想,将连通图用tarhan缩成DAG图然后跑一遍最长路就可以
//下面有几个注意点
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,res,p[N],dist[N],a[N];
int e[N],ne[N],h[N],idx,money[N];
//int _e[N],_ne[N],_h[N],_idx;
int dfn[N],low[N],_stack[N],id[N];
int scc_num,times,top;
vector<int>mp[N]; 
bool vis[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
    low[u]=dfn[u]=++times;
    _stack[++top]=u,vis[u]=true;
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(!dfn[j]) tarjan(j),low[u]=min(low[u],low[j]);
        else if(vis[j]) low[u]=min(low[u],dfn[j]);
    }
    if(low[u]==dfn[u]){
        int y;
        ++scc_num;
        do{
            y=_stack[top--];
            vis[y]=false,money[scc_num]+=a[y],id[y]=scc_num; //链表都是新得了,我们得到的DAG是图是全新的
            //所以此时的变量时SCC_NUM,我们要开一个新的数组记录边权值,这里用money
        }while(y!=u);
    }
}
void topsort()
{
    queue<int>que;
    for(int i=1;i<=scc_num;i++) if(!p[i]) que.push(i),dist[i]=money[i];
    while(!que.empty()){
        int now=que.front(); que.pop();
        for(int i=0;i<mp[now].size();i++){
            int j=mp[now][i];
            p[j]--,dist[j]=max(dist[j],dist[now]+money[j]);
            if(p[j]==0) que.push(j);
        }
    } 
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        add(x,y);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
        for(int j=h[i];~j;j=ne[j]){
            int k=e[j];
            int x=id[i],y=id[k];//这里不是id[j],而是id[i],因为我们要求的是第i个点与其所有邻点是否相通
            if(x!=y) mp[x].push_back(y),p[y]++; //注意点一,一定要重新再建一个链表,而且之前的那个链表不能重置
        }
    topsort();
    for(int i=1;i<=n;i++) res=max(res,dist[i]);
    cout<<res<<endl;
    return 0;
}

 

 

 

 

标签:Tarjan,idx,vis,int,++,dfn,low
From: https://www.cnblogs.com/o-Sakurajimamai-o/p/17615133.html

相关文章

  • tarjan,点双和边双学习笔记。
    发现之前学的真的一塌糊涂呢(/ω\)很多非常精髓的地方理解的都不够好,比如说为啥我要用一棵dfs树来为框架,跑tarjan?这里我就理解的不好,所以我来重新写一篇,加深加深印象。以下一切默认为无向图。0.基本概念这里面说的非常不严谨,只是为了方便理解啦awa连通分量:你可以简单的......
  • Tarjan缩点
    P3225[HNOI2012]矿场搭建一共只会删除一个点,将每个点双连通分量分三种情况讨论第一种:点双连通分量没有割点,那么为了保证一定可以逃出去,至少需要两个点第二种:点双连通分量有且只有一个割点,此处割点是绿色的点,那么对于这种点双连通分量就需要在每个只有一个割点的双连通分量......
  • Tarjan 系列学习笔记
    最近在复习提高算法,所以学习复习笔记写的就比较多。Tarjan系列的算法主要针对于图论而言。Part\(1\)缩点缩点算是Tarjan算法最广泛的应用了。先讲拓扑序。在一个有向图中,若此图无环,我们称这个图是有向无环图,也叫DAG,我们可以用拓扑排序解决许多图上问题,简单思路是先把入......
  • tarjan(dcc-e)
    [冗余路径](395.冗余路径-AcWing题库)考虑无向图的边双连通分量。这个算法也叫Tarjan算法,且与有向图的强连通分量差不多。边双是指图中任意两点间都存在两条不相交的路径(或删去任意一条边后图仍然连通)。桥:切去这条边后,图不连通。由于这是无向图,所以定义中不包含横叉边。......
  • 图论强联通分量(tarjan)算法
    图论强联通分量(tarjan)算法#include<bits/stdc++.h>usingnamespacestd;intn,m,cnt,cntb,ans;vector<int>edge[101];vector<int>belong[101];boolinstack[101];intdfn[101];intlow[101];stack<int>s;voidTarjan(intu){ ++cnt; dfn[u]=......
  • wsr_tarjan
    Tarjan首先是概念:极大强连通分量:不能再加入一点保持整个图强连通的图强连通分量:从任意一点能到达另一任意一点的图Tarjan原理树边 :在树上(图中黑色边)横插边 :从一棵子树到另一棵子树的边(图中绿色边)3、 返祖边 :连到自己的祖先的边观察图,我们可以注意到:横插......
  • LCA 离线tarjan算法
    tarjan算法是离线算法,它必须先将所有的要查询的点对存起来,然后在搜的时候输出结果。tarjan算法很经典,因为算法的思想很巧妙,利用了并查集思想,在dfs下,将查询一步一步的搜出来。伪代码如下:可以看到,对于我们已经保存好的查询,假设为(u,v),u为此时已经搜完的子树的根节点,v的位置就只有两种......
  • tarjan 学习笔记
    tarjan学习笔记求解强联通分量我们从一个点开始建立dfs树,有如下四种边:树边若\(u\)到\(v\)有边,且满足\(v\)没有被访问过,则这条边为树边返祖边若\(u\)到\(v\)有边,且满足\(v\)已被访问过,则这条边为返祖边横叉边若\(u\)到\(v\)有边,且满足\(u\)和......
  • 【学习笔记】Tarjan
    前言:凡事都得靠自己--bobo催隔壁K8Hen天了让他写Tarjan的学习笔记,但貌似还没有动静,所以决定自己写一个。正文本文配套题单:14.图论-tarjan(强连通分量、割点、割边)前置知识熟练使用链式前向星强连通、强连通图、强连通分量的定义(详见oi-wiki,这里不再赘述)如图......
  • BZOJ 2730: [HNOI2012]矿场搭建 tarjan割点
    2730:[HNOI2012]矿场搭建TimeLimit: 10Sec  MemoryLimit: 128MBSubmit: 2010  Solved: 935[Submit][Status][Discuss]Description煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口......