补题计划开始。

题目描述

求一个有向图的最小环。该图所有点的出度均为 \(1\)

数据范围:\(1\le n\le 2 \times 10^5\)

误区

被样例误导,以为该图一定是连通的,于是认为整个图只有一个环,然后利用该性质进行解题。

错误代码很简单,就是找到唯一的环然后计算长度,容易误以为是正解实际只有 \(40 \text{pts}\)

碰到这种错误就 remake 吧。

解法

一个做法是 dfs 染色,这种做法很契合题目的出题意图,我特别想写这种做法但是写挂了。

考虑并查集求最小环。每一次都要判断两点间是否有边。无边则连上,有边则更新最小环大小。

如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和加 \(1\)

代码实现

要直接在并查集上面改哦。具体的看代码。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 200005;
const int inf = 0x7fffffff;

int n, a[maxn];
int f[maxn], d[maxn];	// f 保存祖先节点,d 保存到其祖先节点的路径长
int minn = inf;

int find(int x) {
    if (f[x] != x) {
        int last = f[x];
        f[x] = find(f[x]);
        d[x] += d[last];
    }
    return f[x];
}

void check(int a,int b)
{
    int x = find(a), y = find(b);
    if (x != y) {
        f[x] = y; 
        d[a] = d[b] + 1;
    } else 
        minn = min(minn, d[a] + d[b] + 1); 
    return;
}

int main() {
    cin >> n;
    for(int i = 1; i <= n; i ++) 
        f[i] = i;
    for(int i = 1; i <= n; i ++) {
        int t;
        cin >> t;
        check(i, t); 
    }
    cout << minn << endl;
}