题解 P2746 【[USACO5.3]校园网Network of Schools】

sukimo

2020-06-06 21:57:29

Solution

思路比较经典,先用tarjan算法缩点得到一个DAG。第一个子任务只需统计DAG中入度为$0$的点的个数即为答案,因为如果某点入度为$0$,则只能自己先接收新软件;不为$0$,则可以由其他学校分发。第二个问题稍微复杂一点,如果该DAG已经强连通,则结果为$0$,很显然;否则将入度为$0$的点数设为$x$,出度为$0$的点数设为$y$,则结果为$max\{x,y\}$。为什么呢?首先,一个强连通图必须保证不含出度和入度为$0$的点,否则不符合定义;所以说我们要把这$x+y$个点进行连接。然后一个出度为$0$的点可以和一个入度为$0$的点连一条边,这样就互补了,且尽量最少。但是很明显,有$\mid x-y\mid$个点无法互补,这样就把它们任意和其他点连边,最终结果正是$max\{x,y\}$。 代码: ``` #include<bits/stdc++.h> using namespace std; stack<int>stk;bool in_stk[105],flag=1;int n,edge_cnt,ind,in[105],out[105],low[105],mark[105],fir[105],order[105];struct STR{int next,to;}edge[10005]; void add(int u,int v){ edge[++edge_cnt].next=fir[u];fir[u]=edge_cnt;edge[edge_cnt].to=v; } void tarjan(int x){ order[x]=low[x]=++ind;in_stk[x]=1;stk.push(x); for(int i=fir[x];i;i=edge[i].next) if(!order[edge[i].to]){ tarjan(edge[i].to);low[x]=min(low[x],low[edge[i].to]); } else if(in_stk[edge[i].to])low[x]=min(low[x],order[edge[i].to]); if(order[x]==low[x]){ int rem,node_cnt=0; do{ rem=stk.top();node_cnt++;mark[rem]=x;in_stk[rem]=0;stk.pop(); }while(rem!=x); if(node_cnt==n)flag=0; } } int main(){ int in_0=0,out_0=0;scanf("%d",&n); for(int i=1;i<=n;i++) for(int v;;){scanf("%d",&v);if(!v)break;add(i,v);} for(int i=1;i<=n;i++)if(!order[i])tarjan(i); for(int i=1;i<=n;i++) for(int j=fir[i];j;j=edge[j].next) if(mark[i]!=mark[edge[j].to]){out[mark[i]]++;in[mark[edge[j].to]]++;} for(int i=1;i<=n;i++) if(mark[i]==i){if(!in[i])in_0++;if(!out[i])out_0++;} printf("%d\n%d",in_0,flag?max(in_0,out_0):0); return 0; } ```