并查集回顾

acwing836—合并集合

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+10;
int n,m;
int p[N];

int find(int x)//返回x的祖宗节点
{
    if(x!=p[x])p[x]=find(p[x]); //x只是一个值,
    return p[x];//如果写成p[x]=find(x)是不对的,无限递归也能看出来!  左右应该都是p[x]才对
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)p[i]=i;

    while(m--)
    {
        char a[3];
        int x,y;
        scanf("%s",a);
        scanf("%d%d",&x,&y);
        if(a[0]=='M')
        {
            p[find(x)]=find(y);
        }
        else
        {
            if(p[find(x)]==p[find(y)])
            printf("Yes\n");
            else
            printf("No\n");
        }
    }
    return 0;
}

acwing837-连通块中点的数量

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

const int N=1e5+10;

int n,m,p[N],cnt[N];
//cnt[i]表示节点i所在的连通块的点的数量。
int find(int x)
{
    if(x!=p[x])
        p[x]=find(p[x]);
    return p[x];
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        {p[i]=i;cnt[i]=1;}

    int x,y;
    while(m--)
    {
        char a[4];
        cin>>a;
        if(a[0]=='C')
        {
            cin>>x>>y;
            /*
            if(find(x)!=find(y))//防止重复加边。
            {
                p[find(x)]=find(y);
                cnt[y]+=cnt[x];    
            }
            */
            x = find(x), y = find(y);//x,y是定死的
            if (x != y)
            {
                p[x] = y;
                cnt[y] += cnt[x];
            }
        }
        else if(a[1]=='1')//是否在一个集合里边。
        {
            cin>>x>>y;
            if(find(x)==find(y))
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
        else//连通块中点的数量。
        {
            cin>>x;
            cout<<cnt[find(x)]<<endl;
        }
    }


    return 0;
}

acwing240—食物链

//y总NB
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=5e4+10;

int n,k,p[N],dis[N];

int find(int x)
{
    if(x!=p[x])
    {
        int t=find(p[x]);
        dis[x]+=dis[p[x]];
        p[x]=t;
    }
    return p[x];
}

int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
        p[i]=i;

    int res=0;
    while(k--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        if(b>n||c>n){res++;continue;}

        int x=find(b),y=find(c);
        if(a==1)
        {
            if(x==y)
            {
                if((dis[b]-dis[c])%3)
                res++;
            }
            else
            {
                p[x]=y;
                dis[x]= dis[c]-dis[b];//这样res就不用++了
            }
        }
        else
        {
            if(x==y)//x吃y
            {
                if((dis[b]-dis[c]-1)%3)
                res++;
            }
            else
            {
                p[x]=y;
                dis[x]=dis[c]+1-dis[b];
            }
        }
    }
    cout<<res;
}

kruskal(利用并查集)求最小生成树

//nice 的kruskal
/*
1.将所有边按权重从小到大排序O(mlogm) m常数较小
2.枚举每条边 a <-> b  
    如果a-b不连通将边加入S中去。
*/
//kruskal适用于稀疏图
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1e5+10,M=2e5+10;


int n,m,st[M],p[N];

struct Edge
{
    int a,b,w;
    bool operator <(Edge x)
    {
        return w<x.w;
    }
}eage[M];


int find(int x)
{
    if(x!=p[x])
    {
        p[x]=find(p[x]);
    }
    return p[x];
}

int kruskal()
{
    int res=0;
    int cnt=0;
    for(int i=0;i<m;i++)
    {
        int x=find(eage[i].a),y=find(eage[i].b);
        if(!st[i]&&x!=y)
        {
            p[x]=y;
            res+=eage[i].w;
            cnt++;
            st[i]=1;
        }
    }
    if(cnt<n-1) 
    {
        cout<<"impossible";
        return 2e9;
    }
    else
    return res;
}

int main()
{
    cin>>n>>m;

    for(int i=1;i<=n;i++)
        p[i]=i;

    for(int i=0;i<m;i++)
    {
        int x,y,c;cin>>x>>y>>c;
        eage[i]={x,y,c};
    }

    sort(eage,eage+m);
    int t=kruskal();
    if(t!=2e9)
    cout<<t;
}
posted @ 2020-09-17 15:01  30天CF上蓝!!!  阅读(119)  评论(0编辑  收藏  举报