zoukankan      html  css  js  c++  java
  • 洛谷专题-并查集

    P1196 [NOI2002]银河英雄传说(带权并查集)

    题意:

    有n艘舰依次排序,每次将i及其身后的舰艇合并至j及其所有舰艇之后,每次询问i到j舰艇之间的距离,如果不在一列输出-1

    思路:

    单纯的合并与查询是否在一列操作比较简单,难的在于查询距离

    首先我们需要三个数组fa[i],sum[i],dis[i]分别为i的父亲,i列所有的舰艇数,与其到其父亲的距离

    可能有人会想i到其父亲的距离不都是1嘛,其实在路径压缩过程中,父亲会与实际的情况不符,虽然直接相连但是可能距离并不为1

    现在考虑合并(i,j)操作,每次合并操作只要对第一艘舰艇进行修改就好了,分别修改

    dis数组的修改直接等于sum[j],之后将sum[j]+=sum[i],并将sum[i]=0

    在每次查询时都会进行路径压缩,因此dis[k](k为排在i之后的舰艇)虽然在合并时没有修改,但是会在路径压缩(查询父亲)时修改成到该列第一艘舰艇的距离

    之后在利用前缀后的思想,(dis[i]-dis[j])-1即为连个舰艇之间的舰艇数了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
     using namespace std;
     const int maxn=3e4+10;
     int fa[maxn],sum[maxn],dis[maxn];
     int find(int x)
     {
         if(fa[x]==x)    return x;
         int f1=fa[x],f2=find(fa[x]);
         dis[x]+=dis[f1];
         fa[x]=f2;
         return f2;
     }
     void uni(int x,int y)
     {
         int f1=find(x),f2=find(y);
         fa[f2]=f1;
         dis[f2]=sum[f1];
         sum[f1]+=sum[f2];
         sum[f2]=0;
     }
     int main()
     {
         int t,i,j;
         char op;
         scanf("%d",&t);
         for(int i=1;i<=maxn;i++){
             fa[i]=i;
             sum[i]=1;
         }
         while(t--){
             cin>>op>>i>>j;
             if(op=='M') uni(j,i);
             else{
                 if(find(i)!=find(j))    cout<<-1<<endl;
                 else{
                     cout<<abs(dis[j]-dis[i])-1<<endl;
                 }
             }
         }
        return 0;
     }
    View Code

    P2024 [NOI2001]食物链(种类并查集)

    题意:

    现在有三种生物,ABC,A吃B,B吃C,C吃A,现在依次告诉你一些关系,请说出这些关系中假话的个数(假话的定义为与之前的话矛盾或不符合事实例如A吃A)

    思路:

    对于一对关系,比较难处理的是虽然你知道X吃Y,但是你不知道X跟Y究竟属于什么物种

    那么我们可以建立3*N大小的并查集,分为A,B,C三个部分,代表着A中的生物吃B中的生物等等类推

    对于一个关系比如X跟Y一类,我们在三个集合中分别将二者相连

    对于关系X吃Y,我们就将A中的X与B中的Y相连,还有两个集合中操作类似

    对于每个关系,我们就可以通过判断在一个集合内是否相连,或者在另一个集合内相连来判断正误了

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
     using namespace std;
     const int maxn=2e5+10;
     int fa[maxn];
     int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
     int main()
     {
         int n,k,ans=0,op,x,y;
         scanf("%d%d",&n,&k);
         for(int i=1;i<=3*n;i++) fa[i]=i;
         for(int i=1;i<=k;i++){
             scanf("%d%d%d",&op,&x,&y);
             if(x>n||y>n){
                 ans++;
                 continue;
             }
            if(op==1){
                if(find(x)==find(y+n)||find(y)==find(x+n))    ans++;
                else{
                    fa[find(x)]=find(y);
                    fa[find(x+n)]=find(y+n);
                    fa[find(x+2*n)]=find(y+2*n);
                }
            }
            else{
                if(x==y){ans++;continue;}
                if(find(x)==find(y)||find(x)==find(y+2*n)) ans++;
                else{
                    fa[find(x)]=find(y+n);
                    fa[find(x+n)]=find(y+2*n);
                    fa[find(x+2*n)]=find(y);
                }
            }
         }
        cout<<ans<<endl;
        return 0;
     }
     
    View Code

    P1197 [JSOI2008]星球大战(逆向思维,并查集)

    题意:

    给你一个无向图,每次从图中删去一个点,询问每次删点过后图中连通块的数量

    思路:

    本题可以离线,因此我们采用离线的逆向做法

    怎么个逆向呢?我们假设一开始只有所有删点操作之后的点,并算出连通块个数

    之后每次向图中加入被删除的点,并统计连通块个数

    如果重新对所有点跑一遍的话时间复杂度上一定会炸,对于新加入的点,我们先对当前连通块个数加1,如果遍历该点连接的所有点,如果能够合并,那么就将连通块个数减1

    最后把答案倒序输出就好了

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<cstring>
     using namespace std;
     const int maxn=4e5+1000;
     int flag[maxn],fa[maxn];
     vector<int>a[maxn];
     stack<int> s,q;
     int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
     int main()
     {
         int n,m,k,u,v;
         memset(flag,1,sizeof(flag));
         scanf("%d%d",&n,&m);
         for(int i=1;i<=m;i++){
             scanf("%d%d",&u,&v);
             a[u].push_back(v);
             a[v].push_back(u);
         }
        scanf("%d",&k);
        for(int i=1;i<=k;i++){
            scanf("%d",&u);
            s.push(u);
            flag[u]=0;
        }
        int cnt=n-k; 
        for(int i=0;i<n;i++) fa[i]=i;
        for(int i=0;i<n;i++){
            if(!flag[i])    continue;
            else{
                for(int j=0;j<a[i].size();j++){
                    if(flag[a[i][j]]){
                        int f1=find(a[i][j]),f2=find(i);
                        if(f1!=f2)    fa[f1]=find(fa[f2]),cnt--;
                    }
                }
            }
        }
        q.push(cnt);
        for(int i=1;i<=k;i++){
            cnt++;
            int x=s.top();
            s.pop();
            for(int j=0;j<a[x].size();j++){
                if(flag[a[x][j]]){
                    int f1=find(a[x][j]),f2=find(x);
                    if(f1!=f2)    fa[f1]=find(fa[f2]),cnt--;
                }
            }
            flag[x]=1;
            q.push(cnt);
        }
        while(!q.empty()){
            cout<<q.top()<<endl;
            q.pop();
        }
        return 0;
     }
    View Code

    P1111 修复公路(并查集)

    题意:

    给你n个点,m条无向边,每条边建好都有一个时间,问什么时候各个点能互相可达

    思路:

    将每条边按时间排序,每次加入一条边,看边连接的两点是否在一个连通块内,不在的话合并连通块,看是否总连通块个数为1即可

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
     using namespace std;
     const int maxn1=1e5+10;
     const int maxn2=1e3+10;
     int fa[maxn2],siz[maxn2],n,m;
     struct node{
         int u,v,t;
     }edge[maxn1];
     int cmp(node a,node b){return a.t<b.t;}
     int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
     int main()
     {
         scanf("%d%d",&n,&m);
         for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].t);
         sort(edge,edge+m,cmp);
        for(int i=1;i<=n;i++)    fa[i]=i;
        memset(siz,0,sizeof(siz));
        int cnt=n;
        for(int i=0;i<m;i++){
            int u=edge[i].u,v=edge[i].v,t=edge[i].t;
            int f1=find(u),f2=find(v);
            if(f1!=f2)    fa[f1]=f2,cnt--;
            if(cnt==1){
                cout<<t<<endl;
                return 0;
             }    
        }
        cout<<-1<<endl;
        return 0;
     }
    View Code
  • 相关阅读:
    URIError: Failed to decode param '/%PUBLIC_URL%/favicon.ico'
    Shiro授权管理
    Unable to execute 'doFinal' with cipher instance
    Shiro登录认证
    Shiro权限管理框架
    如此工作
    https真的安全吗,加密登录其实不简单
    奔波中,还是否记得来时路?
    最是伤情
    奈何桥上经过的地方,看醒了多少人
  • 原文地址:https://www.cnblogs.com/overrate-wsj/p/12310300.html
Copyright © 2011-2022 走看看