【洛谷 P1653】 猴子 (并查集)

题目链接
没删除调试输出,原地炸裂,\(80\)->\(0\)。如果你要问剩下的\(20\)呢?答:数组开小了。
这题正向删边判连通性是很不好做的,因为我们并不会并查集的逆操作。于是可以考虑把断边改成逆向连边,某个猴子什么时候和\(1\)号猴子变成连通的,这就是他掉下去的时间,如果本来就与\(1\)号猴子连通,那么它就永远不会掉下去。我用了两个并查集,一个维护连通性,一个维护答案,这两个并查集前面的操作几乎是一模一样的,同时连边,到最后一步也就是某个猴子与\(1\)号猴子变为连通时,前者正常连边,后者不连,因为我们要记录原连通块里的猴子答案是一样的。然后就\(ok\)了。

#include <cstdio>
#include <cstring>
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
const int MAXN = 500010;
inline int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
    return s * w;
}
int n, m;
int a[MAXN], b[MAXN], f[MAXN], ban[MAXN][3], F[MAXN];
int fall_time[MAXN], son[MAXN][3], start_set[MAXN], linked[MAXN];
int junk;
void init(){
    for(int i = 1; i <= n; ++i)
        f[i] = i;
}
int find(int x){
    return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y){
    f[find(y)] = find(x);
}
void Init(){
    for(int i = 1; i <= n; ++i)
        F[i] = i;
}
int Find(int x){
    return F[x] == x ? x : F[x] = Find(F[x]);
}
void Merge(int x, int y){
    F[Find(y)] = Find(x);
}
int main(){
    Open("monkey");
    memset(fall_time, -1, sizeof fall_time);
    n = read(); m = read();
    init(); Init();
    for(int i = 1; i <= n; ++i){
        son[i][1] = read(); son[i][2] = read();
    }
    for(int i = 1; i <= m; ++i){
        a[i] = read(); b[i] = read();
        ban[a[i]][b[i]] = 1;
    }
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= 2; ++j)
            if(!ban[i][j])
                if(son[i][j] != -1)
                    merge(i, son[i][j]), Merge(i, son[i][j]);
    for(int i = m; i; --i){
        junk = son[a[i]][b[i]];
        int fa = find(a[i]) != find(1), fb = find(junk) != find(1);
        merge(a[i], junk);
        if(fa && find(a[i]) == find(1))
            fall_time[Find(a[i])] = i - 1;
        else if(fa) Merge(a[i], junk);
        if(fb && find(junk) == find(1))
            fall_time[Find(junk)] = i - 1;
        else if(fb) Merge(a[i], junk);
    }
    for(int i = 1; i <= n; ++i)
        printf("%d\n", fall_time[Find(i)]);
    Close;
    return 0;
}

转载于:https://www.cnblogs.com/Qihoo360/p/9660259.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值