【洛谷P2746】Network of Schools

时间:2022-04-04 23:09:46

题目大意:给定一个 N 个点,M 条边的有向图,第一问求至少从多少个点出发才能遍历整个有向图,第二问求至少在这个有向图的基础上加多少条边才能使得该无向图强连通。

题解:先进行 Tarjan 缩点,得到一个 DAG。对于这个 DAG 来说,第一问显然是入度为 0 点的个数。第二问中的每条新边均应该是在出度为 0 点和入度为 0 点之间添加,因此答案是入度为 0 的点的个数和出度为 0 点的个数的最大值。另外,若只有一个强联通分量的话,需要特判。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxv=110;
const int maxe=1e4+10; inline int read(){
int x=0,f=1;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
return f*x;
} struct node{
int nxt,to;
}e[maxe];
int tot=1,head[maxv];
inline void add_edge(int from,int to){
e[++tot]=node{head[from],to},head[from]=tot;
} int n;
int dfs_clk,low[maxv],dfn[maxv],stk[maxv],top,in[maxv];
int scc,cor[maxv];
vector<int> G[maxv];
int indeg[maxv],outdeg[maxv],ans1,ans2; void tarjan(int u){
dfn[u]=low[u]=++dfs_clk;
stk[++top]=u,in[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
else if(in[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
++scc;int v;
do{
v=stk[top--],in[v]=0;
cor[v]=scc;
}while(u!=v);
}
} void read_and_parse(){
n=read();
for(int i=1,to;i<=n;i++)while(to=read())add_edge(i,to);
} void solve(){
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
for(int u=1;u<=n;u++)
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(cor[v]==cor[u])continue;
G[cor[u]].push_back(cor[v]),++indeg[cor[v]],++outdeg[cor[u]];
}
for(int i=1;i<=scc;i++){
if(!indeg[i])++ans1;
if(!outdeg[i])++ans2;
}
ans2=scc==1?0:max(ans2,ans1);
printf("%d\n%d\n",ans1,ans2);
} int main(){
read_and_parse();
solve();
return 0;
}