Wannafly挑戰賽14 C-可達性(tarjan縮點+並查集)
阿新 • • 發佈:2018-12-03
思路來源
俊賢大佬
題解
tarjan縮點為無環圖,
每個強連通分量內的點排個序,取出標號最小的那個。
然後我們掃描等價的新圖。
若u和v在新圖裡面不是一個點,即來自不同的連通分量,//這句表達的思想很重要,網路流裡也有應用
且有邊u->v,就令par[v]=u,相當於把入度不為0的點刪掉。
顯然在新圖裡,入度為0的點的集合是最優的。
又因為已按標號排序,所以這些集合第一個就是標號最小的。
難度不難,但感覺思路很6啊,直接扒程式碼懶得自己寫了GG。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<algorithm> #define ll long long using namespace std; const int maxn=2e5+5; int dfn[maxn],low[maxn],stack[maxn],vis[maxn],deep,top,num,s[maxn]; int n,m; struct Edge{ int from; int to; int dist; Edge(int u,int v,int d):from(u),to(v),dist(d){} }; vector<Edge>edges; vector<int>G[maxn]; vector<int>T[maxn]; void AddEdge(int u,int v,int d){ edges.push_back(Edge(u,v,d)); m=edges.size(); G[u].push_back(m-1); } void tarjan(int u){ dfn[u]=low[u]=++deep; stack[++top]=u; vis[u]=1; for (int i=0;i<G[u].size();i++){ Edge& e=edges[G[u][i]]; int v=e.to; if (!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); }else if (vis[v]){ low[u]=min(low[u],dfn[v]); } } if (low[u]==dfn[u]){ num++; do{ T[num].push_back(stack[top]); vis[stack[top]]=0; top--; }while (stack[top+1]!=u); } } int a[maxn],b[maxn],fa[maxn]; int main(){ ios::sync_with_stdio(false); int N,M; cin>>N>>M; for (int i=1;i<=M;i++){ cin>>a[i]>>b[i]; AddEdge(a[i],b[i],1); } for (int i=1;i<=N;i++){ if (!dfn[i]) tarjan(i); } for (int i=1;i<=num;i++){ sort(T[i].begin(),T[i].end()); for (int j=0;j<T[i].size();j++){ s[T[i][j]]=i; } } for (int i=1;i<=num;i++) fa[i]=i; for (int i=1;i<=M;i++){ if (s[a[i]]!=s[b[i]]) { fa[s[b[i]]]=s[a[i]]; } } vector<int>ans; int cnt=0; for (int i=1;i<=num;i++){ if (fa[i]==i){ cnt++; ans.push_back(T[i][0]); } } cout<<cnt<<endl; for (int i=0;i<cnt;i++){ cout<<ans[i]; if (i<cnt-1) cout<<" "; } }