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); }