洛谷 2921 記憶化搜索 tarjan
傳送門 (https://www.luogu.org/problem/show?pid=2921)
做這題的經歷有點玄學,,起因是某個random題的同學突然發現了一個0提交0通過的題目,然后就引發了整個機房的興趣,,然后,,就變成了16提交7通過,,
初看上去這題目就是記憶化搜索,但是環的存在使得普通的記憶化會導致漏解,繼續觀察發現整張圖為n個點n條邊,即是多個基環外向樹,使用tarjan找到圖中的環,顯然可知,對于環上一點,能取到的最大值是環的長度,對于環外一點,能取到的最大值是它走到環的長度加上環長,之后采用記憶化搜索或dp即可得解
Warning:
- 從樣例可以顯然發現,存在自環
- 開始寫tarjan時錯誤的理解了low數組的含義,將其與from數組混淆
#include <cstdio>
#include <cstring>
#include <algorithm>const int maxn = 100000 + 100;
int next[maxn];
int dfn[maxn], low[maxn], size[maxn], sta[maxn];
int from[maxn];
int vis[maxn];
int stackTop = 0;
int tim = 0;
int n;
int ans[maxn];void tarjan(int x) {tim++;stackTop++;sta[stackTop] = x;dfn[x] = low[x] = tim;vis[x] = 1;if (!dfn[next[x]]) {tarjan(next[x]);low[x] = std :: min(low[next[x]], low[x]);} else if (vis[next[x]]) {low[x] = std :: min(low[x], dfn[next[x]]);}if (low[x] == dfn[x]) {while (sta[stackTop] != x) {size[x]++;from[sta[stackTop]] = x;vis[sta[stackTop]] = 0;stackTop--;}vis[x] = 0;stackTop--;size[x]++;from[x] = x;}
}void dfs(int x) {if (ans[x] > 0) return;if (from[x] != x || size[x] > 1) {ans[x] = size[from[x]];return;} else if (next[x] == x) {ans[x] = 1;return;} else {dfs(next[x]);ans[x] = 1 + ans[next[x]];}
}int main () {scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &next[i]);}for (int i = 1; i <= n; i++) {if (!dfn[i]) tarjan(i);}//for (int i = 1; i <= n; i++) // printf("%d\n", from[i]);for (int i = 1; i <= n; i++)if (ans[i] == 0) dfs(i);for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);return 0;
}