题目:https://www.luogu.org/problemnew/show/P2746
分析
用Tarjan求出图中所有的强连通分量并进行缩点。新图中入度为0的点的数量为任务A的结果,入度为0的点的数量和出度为0的点的数量的最大值为任务B的结果。
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=11000;
int n,a;
int cnt;
int u[N],v[N],f[N],next[N];
int nbn[N],ncnt,nbcnt;
int dfn[N],low[N],Index,is_sol[N];
int stack[N],sta_l,is_sta[N];
int q1,q2,in[N],out[N];
void tarjan(int nd){
dfn[nd]=low[nd]=++Index;
stack[++sta_l]=nd;
is_sta[nd]=is_sol[nd]=1;
int ned=f[nd],nnd=v[ned];
while (ned!=0){
if (is_sta[nnd])
low[nd]=min(low[nd],dfn[nnd]);
else if (is_sol[nnd]==0){
tarjan(nnd);
low[nd]=min(low[nd],low[nnd]);
}
ned=next[ned]; nnd=v[ned];
}
if (dfn[nd]==low[nd]){
nbcnt++;
while (1){
is_sta[stack[sta_l]]=0;
nbn[stack[sta_l]]=nbcnt;
sta_l--;
if (stack[sta_l+1]==nd) break;
}
}
}
int main(){
//freopen("Network_of_Schools.in","r",stdin);
scanf("%d",&n);
for (int i=1; i<=n; i++){
while (1){
scanf("%d",&a);
if (a==0) break;
u[++cnt]=i; v[cnt]=a;
next[cnt]=f[i];
f[i]=cnt;
}
}
for (int i=1; i<=n; i++){
if (dfn[i]==0) tarjan(i);
}
int nnn=nbn[1],ans=1;
for (int i=2; i<=n; i++){
if (nbn[i]==nnn) ans++;
}//注意此处对原图为强连通图的特判
if (ans!=n){
for (int i=1; i<=n; i++){
int ned=f[i];
while (ned!=0){
if (nbn[u[ned]]!=nbn[v[ned]])
in[nbn[v[ned]]]=1;
out[nbn[u[ned]]]=1;
ned=next[ned];
}
}
for (int i=1; i<=nbcnt; i++){
if (in[i]==0) q1++;
if (out[i]==0) q2++;
} q2=max(q1,q2);
printf("%d\n%d",q1,q2);
}
else printf("1\n0");
return 0;
}