题解:本题主要考查tarjian缩点+强连通分量。
简要题意:在缩点后的图中,子任务A:求入度为0的点的个数,子任务B:求入度为0的点数与出度为0的点的较大值。
1.tarjan缩点:缩点就是tarjan求模板,一个强连通分块里面可以互相到达,那么可以当成一个点处理。本题最大难点是理解题意,把题目背景转化为计算机语音。
代码如下:
#include<iostream>
using namespace std;
struct E
{
int start,to,from;
}e[565129];
int n,P,num,top,num1,a,ans1,ans2;
int dfn[565129],low[565129],h[565129],in[565129],out[565129],sta[565129],f[565219];
bool v[565129];
void add(int start,int to)
{
e[++P].start=h[start];
e[P].to=to;
e[P].from=start;
h[start]=P;
}
void tarjan(int p)//缩点
{
dfn[p]=low[p]=++num;
sta[++top]=p;v[p]=1;
for(int i=h[p];i;i=e[i].start)
{
int k=e[i].to;
if(!dfn[k])
{
tarjan(k);
low[p]=min(low[p],low[k]);
}
else if(v[k])low[p]=min(low[p],dfn[k]);
}
if(dfn[p]==low[p])
{
num1++;
while(1)
{
int k=sta[top--];
f[k]=p;v[k]=0;
if(p==k)break;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
while(1)
{
cin>>a;
if(a==0)break;
add(i,a);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=1;i<=P;i++)
{
int k=f[e[i].from];int t=f[e[i].to];//记录入出度
if(k!=t){out[k]++;in[t]++;}
}
for(int i=1;i<=n;i++)
{
if(f[i]==i)
{
if(in[i]==0)ans1++;
if(out[i]==0)ans2++;
}
}
if(num1==1)cout<<"1"<<endl<<"0"<<endl;//注意特判!!!
else cout<<ans1<<endl<<max(ans1,ans2)<<endl;
return 0;
}