P10693 撅个题

P10693 撅个题

这个题是一个比较神奇的图论题

首先我们看到题面是这样描述的,第 $ i $ 个人想坐 $ a_i $ 个位置,于是 $ i $ 对 $ a_i $ 连边

手玩个样例会发现,我们建出来的图有以下性质:

  1. 前 $ n $ 个点往外连边,后 $ n $ 个点不往外连边

  2. 可能会存在环

  3. 每个点只会往外连至多一条边

有了这些性质,我们会发现,这个图我们需要考虑到以下三种情况

  1. 对于一条链,他一定会连到后 $ n $ 个点去,但后 $ n $ 个点可能会连不只一条链,所以考虑最长的一条,取它的贡献

  2. 其他的部分就是一个基环树森林,即不只一个基环树,所以我们考虑拓扑排序,把非环部分删掉

  3. 把剩下点的数量加到答案里,这是环,环上的点都可以满足要求

所以我们就做完了

代码如下:

# include <bits/stdc++.h>

using namespace std;

int n;

vector < int > vec[200005], vec1[200005];

bool vis[200005];

int in[200005];

int res = 0, ans = 0;

queue < int > q;

inline void dfs (int u, int d) {

    if (u <= n) res = max (res, d), vis[u] = 1;

    for (int i = 0; i < vec1[u].size (); ++ i) {

        int v = vec1[u][i];

        dfs (v, d + 1);

    }

}

signed main () {

    cin >> n;

    for (int i = 1; i <= n; ++ i) {

        int x; cin >> x;

        vec[i].push_back (x);

        vec1[x].push_back (i);

        ++ in[x];

    }

    for (int i = n + 1; i <= 2 * n; ++ i) {

        res = 0;

        dfs (i, 0);

        ans += res;

    }

    for (int i = 1; i <= n; ++ i) if (! vis[i] && ! in[i]) q.push (i), vis[i] = 1;

    for (; ! q.empty (); ) {

        int u = q.front ();

        q.pop ();

        for (int i = 0; i < vec[u].size (); ++ i) {

            int v = vec[u][i];

            -- in[v];

            if (! in[v]) q.push (v), vis[v] = 1;

        }

    }

    for (int i = 1; i <= n; ++ i) if (! vis[i]) ++ ans;

    cout << ans << endl;

    return 0;

}
posted @ 2024-07-22 14:56  __Tzf  阅读(20)  评论(0编辑  收藏  举报