用时:大概60min
睡前随便水一发博客,摸了
有向图,一个点向其他点连边(传递信息)。
问:1.至少选多少点作为源点,使所有点都能收到信息。2.至少连多少边,使任选一点作为源点,都能使所有点收到消息。
显然每个强联通分量内的点可以互相到达,所以先缩点。
1的答案即为入度为0的点的个数。
2即要使每一点都有入度和出度,答案为入度和出度为0的点的较大值。
注意特判强连通图的情况,因为至少要选一个点。输入1,0即可。
代码如下
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#define Mogeko qwq
using namespace std;
const int maxn = 1e5+10;
int n,m,x[maxn],y[maxn],inn[maxn],out[maxn],ans1,ans2;
int head[maxn],to[maxn],nxt[maxn],cnt;
int dfn[maxn],low[maxn],sta[maxn],col[maxn],top,now,num;
bool insta[maxn];
int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch < ‘0‘ || ch > ‘9‘){
if(ch == ‘-‘) f = -1;
ch = getchar();
}
while(‘0‘ <= ch && ch <= ‘9‘){
x = x*10 + ch-‘0‘;
ch = getchar();
}
return x*f;
}
void add(int x,int y){
to[++cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
}
void clear(){
memset(head,0,sizeof(head));
memset(to,0,sizeof(to));
memset(nxt,0,sizeof(nxt));
}
void tarjan(int u){
dfn[u] = low[u] = ++now;
sta[++top] = u;
insta[u] = true;
for(int i = head[u];i;i = nxt[i]){
int v = to[i];
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(insta[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
col[u] = ++num;
insta[u] = false;
while(sta[top] != u){
int v = sta[top--];
col[v] = num;
insta[v] = false;
}
top--;
}
}
int main(){
n = read();
for(int i = 1;i <= n;i++)
while(1){
int j = read();
if(!j) break;
add(i,j);
m++;
x[m] = i, y[m] = j;
}
for(int i = 1;i <= n;i++)
if(!dfn[i]) tarjan(i);
clear();
for(int i = 1;i <= m;i++)
if(col[x[i]] != col[y[i]]){
out[col[x[i]]]++;
inn[col[y[i]]]++;
}
for(int i = 1;i <= num;i++){
if(!inn[i]) ans1++;
if(!out[i]) ans2++;
}
if(num == 1) printf("1\n0\n");
else printf("%d\n%d\n",ans1,max(ans1,ans2));
return 0;
}