P2746 [USACO5.3]校园网Network of Schools
第二个点想不出来。。。
题目给你一个有向图,然后问你两个问题:
-
至少标记几个点,才能使所有点都被标记。
-
还需要添加几条边,才能使整个图都连通。
其实像考tarjan的题目,大多都跟入度和出度相关联。就像那道“受崇拜的牛”。
直接用tarjan算法进行缩点,得到一个DAG。
有了一个DAG后就可以用入度和出度来讨论一些特定的性质。
第一个问题:发现只要在DAG中,标记拓扑序在最前头的那些点,整个图都会被标记。
而这些点的特征就是:入度为0。
所以统计一下新图中入度为0的点有多少个就可以了。
第二个问题:如果要还要添加边,那么缩完的图就肯定不止一个点。
想要都连通,就要将一个路径的终点和另一个路径的起点相连接。
一条路径的终点的特征?出度为0。起点特征?入度为0。
那么就用最少的边将所有的0出度点和0入度点连接起来。
最少多少?是它们的最大值。
但是注意一个特判:
如果这个图一开始就所有点连通的话,缩点得到的有一个点。
如果按照上面的做法的话,第二个答案会是1,但实际不用添加任何边。
所以特判一下就可以了。
代码:
#include<cstdio>
#include<algorithm>
const int maxn = 105;
struct Edges
{
int next, to;
} e[maxn * maxn], e2[maxn * maxn];
int head[maxn], tot;
int head2[maxn], tot2;
int dfn[maxn], low[maxn], dtot;
bool vis[maxn];
int stack[maxn], top;
int color[maxn], ctot;
int indegree[maxn], outdegree[maxn];
int n;
bool b[maxn][maxn];
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0')
{
if(ch == '-') s = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
ans = ans * 10 + ch - '0';
ch = getchar();
}
return s * ans;
}
void link(int u, int v)
{
e[++tot] = (Edges){head[u], v};
head[u] = tot;
}
void link2(int u, int v)
{
e2[++tot2] = (Edges){head2[u], v};
head2[u] = tot2;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++dtot;
stack[++top] = u; vis[u] = true;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(!dfn[v])
{
tarjan(v);
low[u] = std::min(low[u], low[v]);
}
else if(vis[v]) low[u] = std::min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
ctot++;
while(stack[top] != u)
{
int x = stack[top--]; vis[x] = false;
color[x] = ctot;
}
int x = stack[top--]; vis[x] = false;
color[x] = ctot;
}
}
int main()
{
n = read();
for(int i = 1; i <= n; i++)
{
int temp;
while(2333)
{
temp = read();
if(temp == 0) break;
link(i, temp);
}
}
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
for(int u = 1; u <= n; u++)
{
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(color[u] != color[v] && !b[color[u]][color[v]])
{
link2(color[u], color[v]);
indegree[color[v]]++;
outdegree[color[u]]++;
b[color[u]][color[v]] = true;
}
}
}
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= ctot; i++)
{
if(indegree[i] == 0) ans1++;
if(outdegree[i] == 0) ans2++;
}
printf("%d\n%d\n", ans1, ctot == 1 ? 0 : std::max(ans1, ans2));
return 0;
}