zoukankan      html  css  js  c++  java
  • 并查集(删点)

    传送门

    有 n 棵树,编号 0 到 n - 1 , 本来以为它们是 n 种不同的树,但是我们陆陆续续的得到了 m 条消息,发现之前统计错了。

    消息有如下两种格式:

    • ”M a b" ,编号 a 和 编号 b 的树划分为一种树 ;
    • "S a", 将编号为 a 的树从它所属的种类里拿出来,成为一个新的种类;

    请问最终有多少种树 ?

    Input

    输入文件中有多个测试用例。

    每个测试用例第一行是两个整数 n 和 m(1 ≤ n ≤ 1e5,1 ≤ M ≤ 1e6),随后是 m 行,每行是上述两种格式之一。

    n = 0 和 m = 0 的时候结束。

    Output

    对于每个测试用例,打印一个整数 —— 树的种类。具体格式参照样例。

    Sample Input

    5 6
    M 0 1
    M 1 2
    M 1 3
    S 1
    M 1 2
    S 3
    
    3 1
    M 1 2
    
    0 0

    Sample Output

    Case #1: 3
    Case #2: 2

    主要是删除的时候肯定不能像以前一样删除

    合并操作就是找到找到两个节点的父亲,修改父亲,如果删除就是将该点的父亲重新设置成自己,但是这是不行的,比如1,2,3的父亲都是1,现在删除1,1的父亲还是1,2,3也是1,集合还是1个,正确的应该是2个。

    那删除节点的父亲不设成自己给新申请一个节点当做父亲,比如1,2,3的父亲都是1,在一个集合,现在删除1,申请了4当做1的父亲,2,3父亲都是1,然后Find(2)找2的父亲 2的父亲是1,但是1的父亲是4,所以给2的父亲更新成了4,3同理,所以还不行。

    正确的方法是每一个点都设立一个虚拟父亲比如1,2,3的父亲分别是4,5,6,现在合并1,2,3都在一个集合,那他们的父亲都是4,现在删除1,那就给1重新申请一个节点7

    现在2,3的父亲是4,1的父亲是7,删除成功。

    
    
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=2e6+100;
    int pre[maxn];
    int vis[maxn];
    int n,m;
    int kase=0;
    int id; 
    int find(int x){
        if(pre[x]!=x){
            pre[x]=find(pre[x]);
        }
        return pre[x];
    } 
    void marge(int x,int y){
        int t1=find(x);
        int t2=find(y);
        if(t1!=t2){
            pre[t1]=t2;
        }
    }
    void del(int x){
        pre[x]=id++;
    }
    void inint(){
        for(int i=0;i<n;i++){
            pre[i]=i+n; 
            vis[i]=0;
        }
        for(int i=n;i<=n+n+m;i++){//最多m次操作 
            pre[i]=i;
            vis[i]=0;
        } 
        id=n+n;
    }
    int main(){
        while(~scanf("%d%d",&n,&m)){
            if(n+m==0){
                break;
            }
            inint();
            int x,y;
            char a[maxn];
            for(int i=1;i<=m;i++){
                scanf("%s",a);
                if(a[0]=='M'){
                    scanf("%d%d",&x,&y);
                    marge(x,y);
                }
                else{
                    scanf("%d",&x);
                    del(x);
                }
            }
            int ans=0;
            for(int i=0;i<n;i++){
                int t=find(i);
                if(!vis[t]){
                    ans++;
                    vis[t]=1;
                }
            }
            printf("Case #%d: %d
    ",++kase,ans); 
        } 
    } 
  • 相关阅读:
    C语言初学者代码中的常见错误与瑕疵(22)
    ANSI C (83)和87 ANSI C 这两个标准存在么?
    常见的认证
    python入门(一)
    Altium Designer重装后图标都变白板或都变一样的解决方法
    转:关于S参数的一些理解
    射频与微波测量之S参数
    驻波比
    PCB特征阻抗计算
    函数的形参(非引用形参、指针形参、引用形参)
  • 原文地址:https://www.cnblogs.com/lipu123/p/13961787.html
Copyright © 2011-2022 走看看