ICPC World Finals 2019 E - Dead-End Detector

题意:

给出一张无向图

如果有一条边(u,v)满足从u走到v除了立刻从这条边折返无法走回u,那这条边就是死胡同

现在要对所有的死胡同进行标记,即如果从点u走边e是一个死胡同,就在边e靠近u的入口处标记

这样有的标记是冗余的,要去点

比如如果从a会进入一个标记的死胡同e1,从b会进入一个标记的死胡同e2,从a走e1可以到b然后进入e2,那么在e2的标记就是冗余的

问最少有多少个标记,能让所有的死胡同被标记到

升序输出方案

 

巨麻烦做法

一条链,标记两端

一棵树,标记所有度为1的点

一个环,不用标记

两个环及连接两个环之间的边,不用标记

 

所以

 

tarjan缩点,重新建图之后

在新图上从一个缩环的点为根开始dfs

如果一条边连出去的子树没有环,那么这条边需要在靠近当前根节点的位置标记

如果一条边连出去的子树有环,继续dfs

最后处理所有的树

 

注意一条边两个点,需要标记两次

 

#include<cstdio>
#include<algorithm>

using namespace std; 

#define N 600001

int n,m;
int front[N],to[N<<1],nxt[N<<1],tot=1;
int ui[N],vi[N];

int dfn[N],low[N]; 
int st[N],top;
int col[N],col_cnt,siz[N];

int FRONT[N],TO[N<<1],NXT[N<<1],TOT;
int aa[N<<1],bb[N<<1];
int d[N]; 
bool have[N<<1];

bool vis[N];

struct edge
{
    int a,b;
}e[N];
int ans;

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
}

void ADD(int u,int v,int uu,int vv)
{
    TO[++TOT]=v; NXT[TOT]=FRONT[u]; FRONT[u]=TOT;
    d[u]++;
    aa[TOT]=uu; bb[TOT]=vv;
}

void tarjan(int x,int y)
{

    dfn[x]=low[x]=++tot;
    st[++top]=x;
    int t;
    for(int i=front[x];i;i=nxt[i])
    {
        if(i==(y^1)) continue;
        t=to[i];
        if(!dfn[t])
        {
            tarjan(t,i);
            low[x]=min(low[x],low[t]);
        }
        else low[x]=min(low[x],dfn[t]);
    }
    if(dfn[x]==low[x])
    {
        col_cnt++;
        while(st[top]!=x) 
        {
            col[st[top--]]=col_cnt;
            siz[col_cnt]++;
        }
        col[st[top--]]=col_cnt;
        siz[col_cnt]++;
    }
}

void rebuild()
{
    tot=0;
    for(int i=1;i<=n;++i)
        if(!dfn[i]) tarjan(i,0);
    for(int i=1;i<=m;++i)
    {
        if(col[ui[i]]==col[vi[i]]) continue;
        ADD(col[ui[i]],col[vi[i]],ui[i],vi[i]);
        ADD(col[vi[i]],col[ui[i]],vi[i],ui[i]);
    }
}

bool cmp(edge p,edge q)
{
    if(p.a!=q.a) return p.a<q.a;
    return p.b<q.b; 
}

void solve_tree()
{
    for(int i=1;i<=m;++i)
    {
        if(vis[col[ui[i]]] || vis[col[vi[i]]]) continue;
        if(d[col[ui[i]]]==1 && d[col[vi[i]]]==1)
        {
            e[++ans].a=ui[i];
            e[ans].b=vi[i];
            e[++ans].a=vi[i];
            e[ans].b=ui[i];
        }
        else if(d[col[ui[i]]]==1)
        {
            e[++ans].a=ui[i];
            e[ans].b=vi[i]; 
        }
        else if(d[col[vi[i]]]==1)
        {
            e[++ans].a=vi[i];
            e[ans].b=ui[i];
        }
    }
}

bool dfs(int x,int fa)
{
    vis[x]=true;
    int t;
    bool th=false;
    for(int i=FRONT[x];i;i=NXT[i])
    {
        t=TO[i];
        if(t==fa) continue;
        if(dfs(t,x))
        {
            have[i]=true;
            th=true;
        }
    } 
    return th | (siz[x]!=1);
}

void dfs2(int x,int y)
{
    int t;
    bool th=false;
    for(int i=FRONT[x];i;i=NXT[i])
    {
        t=TO[i];
        if(t==y) continue;
        if(!have[i])
        {
            e[++ans].a=aa[i];
            e[ans].b=bb[i];
        }
        else dfs2(t,x);
    }
}

void solve_circle(int rt)
{
    dfs(rt,0);
    dfs2(rt,0);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&ui[i],&vi[i]);
        add(ui[i],vi[i]);
        add(vi[i],ui[i]);
    }
    rebuild();
    for(int i=1;i<=col_cnt;++i)
    {
        if(vis[i]) continue;
        if(siz[i]!=1) solve_circle(i);
    }
    solve_tree();
    printf("%d\n",ans);
    sort(e+1,e+ans+1,cmp);
    for(int i=1;i<=ans;++i) printf("%d %d\n",e[i].a,e[i].b); 
}

 

posted @ 2020-12-05 17:16  TRTTG  阅读(178)  评论(0编辑  收藏  举报