这题是要求给无向图定向之后最大化每个点能到达的点的个数的最小值
显然定向后如果一些点构成SCC那么是可以互达的,而满足这个条件的点在原图上是个BCC(边双)
简单证明一下这个结论:
一方面,如果存在桥边,那么显然桥边在缩点后会变成一个单向边,两边没法互达;
另一方面,如果一些点构成了BCC,那么根据tarjan算法,只需要把树边向下定向,返祖边向上定向,那么一定可以从根走到叶子再回到根,这显然是个SCC
那么我们跑一遍BCC的tarjan缩点,之后形成了一棵树(树边是桥边)
树的话,可以发现有一个点一定没有出度,如果这个点不是BCC内点数最多的点那么显然不是最小
所以我们把BCC内点数最多的点定为根,其他指向根就行了
复杂度(O(n+m))
1 #include<bits/stdc++.h> 2 #define maxn 400005 3 using namespace std; 4 int n,m; 5 vector< pair<int,int> > g[maxn],Ans,g2[maxn]; 6 vector<int> bcc[maxn]; 7 int pre[maxn],low[maxn],Tim,cnt,isbri[maxn],bel[maxn]; 8 stack<int> stk; 9 void tarjan(int u,int fid) 10 { 11 pre[u]=low[u]=++Tim; 12 stk.push(u); 13 for(auto pa:g[u]) 14 { 15 int v=pa.first,nid=pa.second; 16 if(nid==fid)continue; 17 if(!pre[v]) 18 { 19 tarjan(v,nid); 20 low[u]=min(low[u],low[v]); 21 if(pre[u]<low[v])isbri[nid]=1; 22 else 23 { 24 if(Ans[nid].first==v)swap(Ans[nid].first,Ans[nid].second); 25 } 26 } 27 else 28 { 29 if(pre[v]<pre[u])low[u]=min(low[u],pre[v]); 30 if(Ans[nid].first==u)swap(Ans[nid].first,Ans[nid].second); 31 } 32 } 33 if(low[u]==pre[u]) 34 { 35 ++cnt; 36 while(1) 37 { 38 int x=stk.top();stk.pop(); 39 bcc[cnt].push_back(x); 40 bel[x]=cnt; 41 if(x==u)break; 42 } 43 } 44 } 45 void dfs(int u,int fa) 46 { 47 for(auto pa:g2[u]) 48 { 49 int v=pa.first,nid=pa.second; 50 if(v==fa)continue; 51 if(bel[Ans[nid].first]==u)swap(Ans[nid].first,Ans[nid].second); 52 dfs(v,u); 53 } 54 } 55 int main() 56 { 57 scanf("%d%d",&n,&m); 58 Ans.resize(m+2); 59 for(int u,v,i=1;i<=m;++i) 60 { 61 scanf("%d%d",&u,&v); 62 g[u].push_back(make_pair(v,i)); 63 g[v].push_back(make_pair(u,i)); 64 Ans[i]=make_pair(u,v); 65 } 66 Tim=0; 67 tarjan(1,0); 68 int rt=0; 69 for(int i=1;i<=cnt;++i)if(bcc[i].size()>bcc[rt].size())rt=i; 70 printf("%d ",bcc[rt].size()); 71 for(int u=1;u<=n;++u) 72 { 73 for(auto pa:g[u]) 74 { 75 int v=pa.first,nid=pa.second; 76 if(isbri[nid])g2[bel[u]].push_back(make_pair(bel[v],nid)); 77 } 78 } 79 dfs(rt,0); 80 for(int i=1;i<=m;++i)printf("%d %d ",Ans[i].first,Ans[i].second); 81 }