题目
一些学校联接在一个计算机网络上,学校之间存在软件支援协议,
每个学校都有它应支援的学校名单(A学校支援学校B,并不表示B学校一定支援学校A)。
当某校获得一个新软件时,无论是直接获得还是通过网络获得,
该校都应立即将这个软件通过网络传送给它应支援的学校。
因此,一个新软件若想让所有联接在网络上的学校都能使用,只需将其提供给一些学校即可。
1. 最少需要将一个新软件直接提供给多少个学校,才能使软件能够通过网络被传送到所有学校?
2.最少需要添加几条新的支援关系,使得将一个新软件提供给任何一个学校,所有学校都能通过网络获得该软件。
思路来源
https://www.cnblogs.com/TnT2333333/p/6875680.html
奎神
题解
求强连通分量,缩点。
第一问是零入度点个数;
第二问是Max(零入度点个数,零出度点个数)。
只有一个SCC时特判。
注意:
如果只用incnt==1和outcnt==1判一个SCC,
是无法区分一条链和一个点的情形的
所以只能用tot==1来判第二问
第二问的构造,可采用
从某个入度为0的点①出发,走到一个出度为0的点②,
然后将②连向下一个入度为0的点③,以此类推
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=105;
const int maxm=2e4+5;
int n,m,u,v;
int incnt,outcnt;//最后答案 出度为0点的个数
int head[maxn],cnt;
int low[maxn],dfn[maxn],num;//最早非负祖先时间戳 时间戳
int stack[maxn],top,now;//用数组模拟栈 栈顶 当前栈顶值
int par[maxn],tot;//染色 颜色数
bool In[maxn];//是否在栈中
int in[maxn],out[maxn];//如果不在一个连通分量里 统计出度
int sum[maxn];//每个连通分量里的点的个数
struct edge
{
int to,next;
}e[maxm];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int u)
{
low[u]=dfn[u]=++num;
In[u]=1;
stack[++top]=u;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(In[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])//环的第一个点
{
tot++;
do
{
now=stack[top--];
par[now]=tot;
sum[tot]++;
In[now]=0;
}while(now!=u);
}
}
void init()
{
memset(head,0,sizeof head);
memset(low,0,sizeof low);
memset(dfn,0,sizeof dfn);
memset(par,0,sizeof par);
memset(sum,0,sizeof sum);
memset(In,0,sizeof In);
memset(in,0,sizeof in);
memset(out,0,sizeof out);
cnt=num=top=tot=0;
incnt=outcnt=0;
}
void solve()
{
for(int i=1;i<=n;++i)
if(!dfn[i])dfs(i);
for(int u=1;u<=n;++u)
{
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;//u->v的有向边
//不在同一个连通分量里
if(par[u]!=par[v])
out[par[u]]++,in[par[v]]++;
}
}
for(int i=1;i<=tot;++i)//枚举连通分量
{
if(!in[i])incnt++;
if(!out[i])outcnt++;
}
}
int main()
{
while(~scanf("%d",&n))
{
init();
for(int i=1;i<=n;++i)
{
int x;
while(~scanf("%d",&x)&&x)
add(i,x);
}
solve();
if(tot==1)puts("1"),puts("0");//注意特判一个SCC
else printf("%d\n%d\n",incnt,max(incnt,outcnt));
}
return 0;
}