P8059 [POI 2003] Monkeys

P8059 [POI 2003] Monkeys

大意

树上挂着 \(𝑛\) 只可爱的猴子,编号为 \(1,2,⋯,𝑛\)。猴子1的尾巴挂在树上,每只猴子有两只手,每只手可以最多抓住一只猴子的尾巴。所有的猴子都是悬空的,因此如果一旦脱离了树,猴子会立刻掉到地上。在第 \(0,1,⋯,m − 1\) 秒钟某个猴子会把它的某只手松开,因此常常有猴子掉到地上。现在请你根据这些信息,计算出每个猴子掉在地上的时间。

思路

对于这个题来说,明显描述的是一些约束关系,我们可以考虑这样的问题,如果什么情况下猴子会掉落,即如果在不断松手的过程中,猴子 \(i\) 与编号为 \(1\) 的猴子分离,即 find(i) != find(1),但是我们的并查集并不能高效快捷的处理断开的操作,我们不妨转换思路,去反着加边

如果在当前的时间,编号为 \(i\) 的猴子加入了 \(1\) 所在的集合,说明在这个时刻会掉落,我们需要另开一个并查集维护根节点的时间即可。

代码

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

const int MAXN = 2e5 + 5;
const int MAXM = 4e5 + 5;

int m, n;
int f[MAXN], t[MAXN], ans[MAXN];
int op[MAXM][2];
int rl[MAXN][3];
bool h[MAXN][3];

int fd(int x){
    return f[x] == x ? x : f[x] = fd(f[x]);
}

int tfd(int x){
    return t[x] == x ? x : t[x] = tfd(t[x]);
}

void fcomb(int x, int y){
    f[fd(y)] = fd(x);
}

void tcomb(int x, int y){
    t[tfd(y)] = tfd(x);
}

int main(){
    cin >> n >> m;
    for (int i = 1;i <= n;i ++){
        cin >> rl[i][1] >> rl[i][2];
        ans[i] = -1;
    }
    for (int i = 0;i < m;i ++){
        cin >> op[i][0] >> op[i][1];
        h[op[i][0]][op[i][1]] = true;
    }
    for (int i = 1;i <= n;i ++){
        f[i] = t[i] = i;
    }
    for (int i = 1;i <= n; i++){
        if (!h[i][1] && rl[i][1] != -1) fcomb(i, rl[i][1]);
        if (!h[i][2] && rl[i][2] != -1) fcomb(i, rl[i][2]);
    }
    for (int i = 1;i <= n;i ++){
        t[i] = f[i];
    }
    for(int i = m - 1;i >= 0;i --){
        int x = op[i][0], y = rl[x][op[i][1]];
        if (y == -1) continue;
        int fx = fd(x), fy = fd(y);
        if (fx == fy) continue;
        if (fx == fd(1)) ans[fy] = i;
        else if (fy == fd(1)) ans[fx] = i;
        else tcomb(x, y);
        fcomb(fx, fy);
    }
    for(int i = 1;i <= n;i ++){
        cout << ans[tfd(i)] << '\n';
    }
    return 0;
}
posted @ 2025-12-16 22:09  To_Carpe_Diem  阅读(7)  评论(0)    收藏  举报