题意
给定一个奶牛数,初始时奶牛在位置上,每个位置可以移动到位置上,求每个奶牛移动的最多次数(对于一个奶牛,一个点至多访问一次).
分析
由于本题一个点只有一条出边,所以图构成的环应该是一个简单环,且一个环上可以收集的价值相同。
应用缩点后的思想,将图分为链与简单环进行处理
- 维护两个染色变量,分别处理环与加入环的链
- 环通过维护路径长度,优先处理环
- 当时,该环收集的价值(可以理解为的访问后向边两次时间戳的差值)
- 确保每个环只处理一次
- 若点不在环上(没被优先处理()),则
PS:还有更猛的做法: 1°记录每个点入度. 2°做一次,确定深度优先森林,同时每个点的入度都减1. 3°还有入度的点即对应了一条后向边的点,从而预处理环(这里运用了环为简单环的性质). 4°对链dp即可
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1e5 + 5;
int N;
int e[MAXN], cnt[MAXN], dis[MAXN];
bool pre_vis[MAXN], now_vis[MAXN];
void dfs(int, int);
int main(){
freopen("indata.txt", "r", stdin);
freopen("outdata.txt", "w", stdout);
ios::sync_with_stdio(false);
int i;
cin >> N;
for(i = 1; i <= N; i++) cin >> e[i];
for(i = 1; i <= N; i++)
if(!pre_vis[i]) dfs(i, 0);
for(i = 1; i <= N; i++) cout << cnt[i] << endl;
return 0;
}
bool circle_vis[MAXN];
void manage_circle(int, int);
void dfs(int u, int times){
if(now_vis[u]) return;
if(pre_vis[u]){
manage_circle(u, times - dis[u]);
return;
}
pre_vis[u] = true, dis[u] = times;
dfs(e[u], times + 1);
if(!cnt[u]) cnt[u] = cnt[e[u]] + 1;
now_vis[u] = true;
}
void manage_circle(int u, int num){
if(circle_vis[u]) return;
circle_vis[u] = true, cnt[u] = num;
manage_circle(e[u], num);
}