Description
你家乡的议会决定对一些道路标志的安置进行改进,特别是一些断头路。他们给了你一个地图,你必须确定在哪里贴上「此路不通」标志,他们希望你使用的标志尽可能少。
地图是由双向街道连接一些地点而形成的集合。以下规则描述了在一个街道 $S$的入口 $x$安放一个「此路不通」标志的条件:如果在从 $x$点进入街道 $S$后,只能通过掉头的方式回到$x$,就应该安装一个「此路不通」标志。定义一个「掉头」操作为做一个 $180$度的转弯,即立刻反转行车的方向。
为了节省成本,你决定不安装任何多余的标志。如果一个街道 $S$的入口$x$ 有一个「此路不通」标志,另一条街道$T$ 的入口$y$ 有一个「此路不通」标志,如果从$x$ 点进入街道 $S$,并能在不掉头的情况下经过 $y$点进入街道$T$ ,那么 $T$的入口 $y$处的标志就是多余的。
Solution
对于有环的连通块,符合要求的边不指向环的边
对于无环的连通块,符合要求的边为叶子边
拓扑排序即可
#include<algorithm> #include<iostream> #include<utility> #include<cstring> #include<vector> #include<cstdio> #include<queue> using namespace std; int n,m,du[500005],tot; bool vst[500005]; vector<int>G[500005]; queue<int>q; pair<int,int>ans[500005]; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return f*w; } int main() { n=read(); m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(); G[u].push_back(v); G[v].push_back(u); ++du[u]; ++du[v]; } for(int i=1;i<=n;i++) { if(du[i]==1) { q.push(i); vst[i]=true; } } while(q.size()) { int u=q.front(); q.pop(); for(auto v:G[u]) { if(!vst[v]&&--du[v]==1) { vst[v]=true; q.push(v); } } } memset(vst,false,sizeof(vst)); for(int i=1;i<=n;i++) { if(du[i]>=2) { q.push(i); vst[i]=true; } } while(q.size()) { int u=q.front(); q.pop(); for(auto v:G[u]) { if(!vst[v]&&du[v]==1) { vst[v]=true; q.push(v); } } } for(int i=1;i<=n;i++) { for(auto v:G[i]) { if(vst[i]&&du[i]>1&&du[v]==1) { ans[++tot]=make_pair(i,v); } if(!vst[i]&&G[i].size()==1) { ans[++tot]=make_pair(i,v); } } } sort(ans+1,ans+tot+1); printf("%lld ",tot); for(int i=1;i<=tot;i++) { printf("%lld %lld ",ans[i].first,ans[i].second); } return 0; }