0%

100075. 有向图访问计数

100075.有向图访问计数

题目描述:

现有一个有向图,其中包含 n 个节点,节点编号从 0n - 1 。此外,该图还包含了 n 条有向边。

给你一个下标从 0 开始的数组 edges ,其中 edges[i] 表示存在一条从节点 i 到节点 edges[i] 的边。

想象在图上发生以下过程:

  • 你从节点 x 开始,通过边访问其他节点,直到你在 此过程 中再次访问到之前已经访问过的节点。

返回数组 answer 作为答案,其中 answer[i] 表示如果从节点 i 开始执行该过程,你可以访问到的不同节点数。

数据范围:

$2\le n \le 10^5$

无自环。

题解:

首先, $i\rightarrow edges[i]$ 表示每个点只有一个出边。并且 $n$ 个点, $n$ 条边,是一棵基环树。

由于每个点只有一个出边,基环外面的点都是指向基环的,即内向基环树。

由于不一定是连通的,可能是基环树森林。

环上的点,答案是环的大小,环外的点,答案是深度加环的大小。

可以直接先 dfs 求出来环的大小,然后将环上的点更新答案。

然后可以从环上的点对不在环上的点反图 $dfs$ ,求出来每个节点的深度。

也可以直接正向dfs,每次遇到已经填过答案的返回。

之前没发现是基环树,而且没有发现每个点只有一个出边。想成了一般的有向图,先缩点,再拓扑排序,然后再使用 $bitset$ 进行 $dp$ ,类似164. 可达性统计 | Luobuyu’s Blog

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
vector<vector<int>> g, rg;
vector<int> vis; // 0, 1, 2未访问,正在访问,访问过
vector<int> ans;
vector<int> dfn;
int dfn_clock = 0;
vector<int> st;
int top = -1;
// dfs 找环
void dfs(int u)
{
vis[u] = 1;
dfn[u] = ++dfn_clock;
st[++top] = u;
for (int i = 0; i < g[u].size(); ++i)
{
int v = g[u][i];
if (vis[v] == 1)
{
int size = dfn[u] - dfn[v] + 1;
while (true)
{
ans[st[top]] = size;
if (st[top] == v)
break;
--top;
}
}
else if (vis[v] == 0)
{
dfs(v);
}
}
vis[u] = 2;
}
int dfs2(int u)
{
if (ans[u])
return ans[u];
for (int i = 0; i < g[u].size(); ++i)
{
int v = g[u][i];
ans[u] = dfs2(v) + 1;
}
return ans[u];
}
vector<int> countVisitedNodes(vector<int> &edges)
{
int n = edges.size();
g.resize(n), rg.resize(n);
dfn.resize(n), ans.resize(n);
vis.resize(n);
st.resize(n);
for (int i = 0; i < n; ++i)
{
int u = i, v = edges[i];
g[u].emplace_back(v);
rg[v].emplace_back(u);
}
for (int i = 0; i < n; ++i)
{
if (!dfn[i])
dfs(i);
}
for (int i = 0; i < n; ++i)
{
if (!ans[i])
{
dfs2(i);
}
}
return ans;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
vector<vector<int>> g, rg;
vector<int> indeg;
vector<int> ans;
void dfs(int u, vector<int> &ring)
{
ring.emplace_back(u);
indeg[u] = -1;
for (int i = 0; i < g[u].size(); ++i)
{
int v = g[u][i];
if (indeg[v] == 1)
dfs(v, ring);
}
}
void dfs2(int u)
{
for (int i = 0; i < rg[u].size(); ++i)
{
int v = rg[u][i];
if (indeg[v] == 0)
{
ans[v] = ans[u] + 1;
dfs2(v);
}
}
}
vector<int> countVisitedNodes(vector<int> &edges)
{
int n = edges.size();
g.resize(n), rg.resize(n);
indeg.resize(n), ans.resize(n);
for (int i = 0; i < n; ++i)
{
int u = i, v = edges[i];
g[u].emplace_back(v);
rg[v].emplace_back(u);
indeg[v]++;
}
queue<int> q;
for (int i = 0; i < n; ++i)
{
if (indeg[i] == 0)
q.push(i);
}
while (q.size())
{
int u = q.front();
q.pop();
for (int i = 0; i < g[u].size(); ++i)
{
int v = g[u][i];
indeg[v]--;
if (indeg[v] == 0)
{
q.push(v);
}
}
}
for (int i = 0; i < n; ++i)
{
if (indeg[i] == 1) // 环上的点
{
vector<int> ring;
dfs(i, ring);
int size = ring.size();
for (auto &u : ring)
{
ans[u] = size;
dfs2(u);
}
}
}
return ans;
}
};