明辨是非 [并查集+启发式合并]

明辨是非


color{blue}{最初想法}

  • 相等有传递性

  • 不相等没有传递性

  • 使用 并查集 维护两两之间相等的数字

  • 对每个祖先开一个 setset, 当两个数字不能相等时, 他们的祖先setset互相记录对方的祖先.

考试的时候忘了合并 …


color{red}{正解部分}

  • 判断是否相等时, 直接看两者的 祖先setset 中有没有记录对方 .
    若两者可以相等, 则 启发式合并 二者 setset .

判断不等直接看祖先是否相等就可以了 .

所谓启发式合并, 就是将 sizesize 小的合并到 sizesize 大的 .


color{red}{实现部分}

没什么好说的 .

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 1e5 + 5;

int N;
int cnt;
int F[maxn];

std::map <int, int> Mp;
std::set <int> st[maxn];

int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]); }

void Work(){
        int x = read(), y = read(), p = read();
        if(!Mp.count(x)) Mp[x] = ++ cnt, F[cnt] = cnt;
        if(!Mp.count(y)) Mp[y] = ++ cnt, F[cnt] = cnt;
        int t1 = Mp[x], t2 = Mp[y];
        if(p == 1){
                int f1 = Find(t1), f2 = Find(t2);
                if(f1 == f2){ printf("YES
"); return ; }
                if(st[f1].count(f2) || st[f2].count(f1)){ printf("NO
"); return ; }

                if(st[f1].size() > st[f2].size()) std::swap(f1, f2); 
                
                for(reg std::set<int>::iterator i = st[f1].begin(); i != st[f1].end(); i ++) st[f2].insert(*i), st[*i].insert(f2);

                F[f1] = f2;
                printf("YES
");
        }else{
                int f1 = Find(t1), f2 = Find(t2);
                if(f1 == f2){ printf("NO
"); return ; }

                st[f1].insert(f2), st[f2].insert(f1);

                printf("YES
");
        }
}

int main(){
        N = read();
        for(reg int i = 1; i <= N; i ++) Work();
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822534.html