这几道题都是 Tarjan 和其他算法的综合,难度较大,含金量较高,特别是第二题。
我的博客:关于 Tarjan 算法的入门博客。
开始——
例题一 受欢迎的牛
思路
概述: Tarjan + 缩点 DAG + 出入度
1. Tarjan
在一个强连通分量中,很明显,所有奶牛都是互相喜欢的,所以我们分别求出每一个强连通分量,并记录下他们的 size 。
2. 缩点 DAG
我们把强连通分量看成一个超级点,并在相互之间建边。
(图片附在第 3 点中。)
缩点是什么?
如果你没接触过缩点,没有关系,缩点算法博客 + 很好的阅读体验 。
如何建边?
我们输入时存下每一条边的 u 和 v ,然后现在判断他们是否属于同一强连通分量中,如果不属于,就在他们之间建边。注意,在这之前要先清空结构体 e ,数组 hd 以及变量 cnt 。
这样,缩完点之后它就是一个 DAG ——有向无环图,我们运用的就是它不成环的特质。
但是!!!这道题不用这么麻烦!
我们在思想上把它们重新建边了,但在实现过程中,我们只需要判断两点是否在同一强连通分量里,如果没有,就进行建边之后的操作(具体见第 3 点或代码);如果有,那可以直接忽略。
缩点有什么用?
缩点之后我们可以大大简化这道题了。对于每一个超级点(也就是一个强连通分量),我们有进一步关于出度的操作了。
3. 出度的运用
接下来我们把图看成一个只有 3 个点的 DAG 。
首先放几张做了二十分钟的百万图片 (wtcl)。
ps:关于图片,看不清请打开一个新标签页,放大看。由于本人第一次画图,所以丑了点…图 1 中有一条画错的边,已改正,正确边为从 E 到 G 。
从上图不难看出,有 3 个强连通分量,我们的第一步操作为反向建边,操作之后图为这样。
出度有什么作用?
它就是我们用来找出符合条件的超级点。
因为每一个超级点要被所有的点(也就是牛)所喜欢,那么它必须满足出度为 0 。
假设我们有一条边为 ( u , v ) (u, v) (u,v) ,表示从 u 到 v ,此时 u 的出度为 1 , v 的出度为 0 。
只有一个超级点出度为 0 ,也就是它没有喜欢的奶牛了,才可能是受欢迎的牛。因为此图没有环,所以它的喜欢不会回来,它也就没有受到所有牛的喜欢。
同时,若该图有两个或两个以上的出度为 0 的点,此题答案依旧为 0 。为什么?还是那句话,因为现在的 DAG 不成环。相信不用我再说,这点已经很好理解了。
为什么要反向建边?
若是正常地建边,我们在计算出度的时候会较为麻烦,所以反向建边,这样一来在统计出度的时候就可以变成“统计入度” 了,相对来说简单很多。
最终,我们的图会变成这样。
这题说到这里,相信已经不用我再说什么了。(好累啊,逃)
数据范围也没有什么特别的,正常设就行。
最后,代码附上。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50005;
int n, m;
int cnt, hd[maxn];
struct node{
int to, nxt;
}e[maxn * 2];
int dfn[maxn], low[maxn];
int top, st[maxn], de[maxn], si[maxn];
int col, co[maxn];
int tmp;
void add (int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = hd[u];
hd[u] = cnt;
}
void tarjan (int u)
{
dfn[u] = low[u] = ++tmp;
st[++top] = u;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan (v);
low[u] = min (low[u], low[v]);
}
else if (!co[v]) low[u] = min (low[u], dfn[v]);
}
if (dfn[u] == low[u])
{
co[u] = ++col;
++si[col];
while (st[top] != u)
{
++si[col];
co[st[top]] = col;
--top;
}
--top;
}
}
int main ()
{
scanf ("%d %d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf ("%d %d", &u, &v);
add (v, u);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i]) tarjan (i);
}
for (int i = 1; i <= n; i++)
{
for (int j = hd[i]; j; j = e[j].nxt)
{
int v = e[j].to;
if (co[i] != co[v]) de[co[v]]++;
}
}
int ans, u;
ans = u = 0;
for (int i = 1; i <= col; i++)
{
if