1. 程式人生 > 實用技巧 >ICPC World Finals 2019 E - Dead-End Detector

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