学校网络(含证明)
欧拉回路的证明我都没写,这个我却写了,哎。
题目
做法
第一问其实就是让你求用强连通缩点之后入度为\(0\)的点。
不难证明的一件事情是,缩点之后是个\(DAG\),而\(DAG\)必然存在入度为\(0\)的点(如果不存在,你从一个点出发一直走指向你的边,最后就会走成一个环。)。
入度为\(0\)的点是肯定需要放的,没什么好说的。但是单单放了入度为\(0\)的点就行了吗?一个点一直走父亲边就可以到达入度为\(0\)的点。
第二问的话,设入度为\(0\)的点集为\(P\),出度为\(0\)的点集为\(Q\),那么答案就是:\(max(|P|,|Q|)\)
惊人的发现我的证法是普通证法的复杂版。
但是为了偷懒直接写普通证法算了
反正至少曾经我想到过证明
参照博客:https://www.acwing.com/solution/content/4663/
首先,先说要实现最小边数的条件。
我们知道一条有向边可以贡献一个入度,一个出度,相应的,也就可以消掉一个\(P\)的点和一个\(Q\)的点,所以至少要\(max(|P|,|Q|)\)
分类讨论。
- \(|P|=1\),此时将\(Q\)中所有的点连向\(P\)即可,所用的边的数量为\(|Q|\)
- \(|Q|=1\),此时将\(Q\)连向\(P\)中所有的点即可,所用的边的数量为\(|P|\)。
- 其余的情况,我们只需要将\(Q\)中的一个点\(q_1\)连向\(P\)中的一个点\(p_2\),那么这个时候指向\(q_1\)的的点就会全部指向\(p_2\)所指向的点,此时刚好把\(q_1\)和\(p_2\)从\(P,Q\)中毫无痕迹的删除,然后重复此操作到删除了\(min(|P|,|Q|)-1\)对点后,开始进入\(1,2\)操作。
这样的话就证明了一定存在一种方案是\(max(|P|,|Q|)\)的。
两者一结合,便证明此结论了。
当然,对于入度出度都为\(0\)的点,你可能会说连一条边只会删掉一个点,但是其实你看这个点啊,拆点,将出去的边和进来的边分开来,这样在对外显示上是没有任何问题的,而且可以帮助你更形象的理解,当然你也可以更硬核的理解,就是\(P,Q\)中都有这个点,这个理解在代码时思路会更加清晰,但是结论就摆在那,入度出度都为\(0\)的点并不会影响结论。
当然,如果整个图就是一个分量的话,答案是\(0\),特判一下即可。
#include<cstdio>
#include<cstring>
#define N 110
#define M 110000
using namespace std;
inline int mymin(int x,int y){return x<y?x:y;}
inline int mymax(int x,int y){return x>y?x:y;}
struct node
{
int y,next;
}a[M];int len,last[N];
inline void ins(int x,int y){len++;a[len].y=y;a[len].next=last[x];last[x]=len;}
int dfn[N],low[N],n,in[N]/*入度*/,out[N]/*出度*/,ti,be[N],sta[N],top,block;
void dfs(int x)
{
sta[++top]=x;dfn[x]=low[x]=++ti;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(!dfn[y])
{
dfs(y);
low[x]=mymin(low[x],low[y]);
}
else if(!be[y])low[x]=mymin(low[x],low[y]);
}
if(dfn[x]==low[x])
{
block++;
while(sta[top]!=x)
{
be[sta[top--]]=block;
}
be[sta[top--]]=block;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
while(1)
{
scanf("%d",&x);
if(x==0)break;
ins(i,x);
}
}
for(int i=1;i<=n;i++)if(!dfn[i])dfs(i);
for(int i=1;i<=n;i++)
{
for(int k=last[i];k;k=a[k].next)
{
int y=a[k].y;
if(be[i]!=be[y])in[be[y]]++,out[be[i]]++;
}
}
int ans1=0,ans2=0;
for(int i=1;i<=block;i++)
{
if(!in[i])ans1++;
if(!out[i])ans2++;
}
if(block==1)printf("1\n0\n");
else printf("%d\n%d\n",ans1,mymax(ans1,ans2));
return 0;
}