[Poi2003/BZOJ2610] Monkeys 题解

题目描述

黑暗爆炸OJ

思路

猴子 \(i\) 掉下去用科学地语言来说就是 \(i\)\(1\) 号点不在一个集合中。

正向地来做,删边操作不好实现,因此每次暴力建一个图,这样时间复杂度太高了,不能接受。

可以发现每次暴力建出来的图实际上只有 “一边之差”,那么我们可不可以利用这个性质去优化时间复杂度呢?

实际上,可以考虑逆向地来做这道题,这样可以发现猴子每次松手反过来只是在逆序图上加了一个边而已,因此可以逆序离线处理每次查询,这么做时间复杂度就得到了极大的优化。

梳理一下思路

  1. 先处理出来所有猴子松手后的最终图;
  2. 逆序处理,如果此次加边连通了 \(1\) 和集合 \(A\),那么意味着集合 \(A\) 中的所有猴子在此刻掉了下去,用 \(\text{DFS}\) 把集合 \(A\) 中所有元素的答案更新一遍;
  3. 输出答案。

总时间复杂度:\(O(m\alpha (n) + n)\)\(O(m\log n + n)\)

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

const int N = 2e5 + 10, M = 4e5 + 10;

int hand[N][3], off[M][3];
bool is_off[N][3];
int fa[N], ans[N], rk[N];
int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

vector<int> g[N];

void merge(int a, int b)
{
    int x = find(a), y = find(b);
    g[a].push_back(b);
    g[b].push_back(a);
    if (x != y)
    {
        if(rk[x] < rk[y]) fa[x] = y;
        else if(rk[x] > rk[y]) fa[y] = x;
        else fa[x] = y, rk[y] ++;
    }
}

bool st[N];
void dfs(int p, int tm)
{
    ans[p] = tm, st[p] = true;
    for (auto i : g[p])
    {
        if (!st[i])
            dfs(i, tm);
    }
    st[p] = false;
}

int main()
{
    int n, m;
    cin >> n >> m;
    memset(ans, -1, sizeof ans);
    for (int i = 1; i <= n; i++)
        cin >> hand[i][1] >> hand[i][2], fa[i] = i, rk[i] = 1;
    for (int i = 1; i <= m; i++)
    {
        cin >> off[i][1] >> off[i][2];
        is_off[off[i][1]][off[i][2]] = true;
    }
    for (int i = 1; i <= n; i++)
    {
        if (!is_off[i][1] && ~hand[i][1])
            merge(i, hand[i][1]);
        if (!is_off[i][2] && ~hand[i][2])
            merge(i, hand[i][2]);
    }

    for (int i = m; i >= 1; i--)
    {
        int a = hand[off[i][1]][off[i][2]], b = off[i][1];
        swap(a, b);
        int x = find(a), y = find(b);
        if (x == y)
            continue;
        if (x == find(1))
        {
            dfs(y, i - 1);
            merge(x, y);
        }
        else if (y == find(1))
        {
            dfs(x, i - 1);
            merge(x, y);
        }
        else
            merge(x, y);
    }
    for (int i = 1; i <= n; i++)
        cout << ans[i] << '\n';

    return 0;
}
posted @ 2022-12-29 09:18  MoyouSayuki  阅读(28)  评论(0编辑  收藏  举报
:name :name