[USACO08DEC]在农场万圣节Trick or Treat on the Farm【Tarja缩点+dfs】

时间:2024-06-21 17:07:38

题目描述

每年,在威斯康星州,奶牛们都会穿上衣服,收集农夫约翰在N(1<=N<=100,000)个牛棚隔间中留下的糖果,以此来庆祝美国秋天的万圣节。
由于牛棚不太大,FJ通过指定奶牛必须遵循的穿越路线来确保奶牛的乐趣。为了实现这个让奶牛在牛棚里来回穿梭的方案,FJ在第i号隔间上张贴了一个“下一个隔间”Next_i(1<=Next_i<=N),告诉奶牛要去的下一个隔间;这样,为了收集它们的糖果,奶牛就会在牛棚里来回穿梭了。
FJ命令奶牛i应该从i号隔间开始收集糖果。如果一只奶牛回到某一个她已经去过的隔间,她就会停止收集糖果。
在*停止收集糖果之前,计算一下每头奶牛要前往的隔间数(包含起点)。
(翻译来自洛谷)

分析

比较容易可以想到,如果一个奶牛位于一个环中,那么它的答案就是这个环的大小。
如果一个奶牛不在一个环中,那么答案就是到它最近的环的距离再加上环的大小。

代码

#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
#define db double
#define N 100005
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1; char ch = 0;
    for (; ch < '0' || ch > '9'; ch = getchar())
        if (ch == '-') fl = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar())
        x = (x << 1) + (x << 3) + (ch ^ 48);
    x *= fl;
}
struct edge {
    int to, nt;
}E[N];
int H[N], dfn[N], vis[N], S[N], nxt[N], belong[N], low[N], ans[N], sum[N];
int scc, cnt, dfn_tot, top, n;
void add_edge(int u, int v) {
    E[++ cnt] = (edge){v, H[u]};
    H[u] = cnt;
}
void tarjan(int u) {
    vis[u] = 1;
    low[u] = dfn[u] = ++ dfn_tot;
    S[top ++] = u;
    for (int e = H[u]; e; e = E[e].nt) {
        int v = E[e].to;
        if (!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
        else if (vis[v]) low[u] = min(low[u], low[v]);
    }
    int j;
    if (dfn[u] == low[u]) {
        scc ++;
        do {
            j = S[-- top];
            belong[j] = scc;
            vis[j] = 0;
        } while (u != j);
    }
}
void dfs(int u, int nt, int dis) {
    if (ans[nt] != 0) {
        ans[u] = ans[nt] + dis;
        return;
    }
    else dfs(u, nxt[nt], dis + 1);
}
int main() {
    read(n);
    for (int i = 1; i <= n; i ++) {
        read(nxt[i]);
        add_edge(i, nxt[i]);
        if (nxt[i] == i) ans[i] = 1;
    }
    for (int i = 1; i <= n; i ++)
        if (!dfn[i]) tarjan(i);
    for (int i = 1; i <= n; i ++) sum[belong[i]] ++;
    for (int i = 1; i <= n; i ++)
        if (sum[belong[i]] != 1) ans[i] = sum[belong[i]];
    for (int i = 1; i <= n; i ++)
        if (ans[i] == 0) dfs(i, nxt[i], 1);
    for (int i = 1; i <= n; i ++) printf("%d\n", ans[i]);
    return 0;
}