并查集

6-1 并查集
///find函数可能有点难理解,自己尝试画下图随便理解好吧
///M是合并,Q是询问是否在一个树中
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m;
int q[N];
int find(int x)
{
    if(q[x]!=x) q[x]=find(q[x]));///如果x不是树的头结点,向上循环查找到头结点为止.
    ///可以自己画图理解一下;
    return q[x];
}
int  main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        q[i]=i;
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",&op,&a,&b);
        if(op[0]=='M') q[find(a)]=find(b);
        else
        {
            if(find(a)==find(b))
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
        }
    }
    return 0;
}
6-2 查并集,但是可能输出长度
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
int p[N],size[N];
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
        size[i]=1;
    }
    while(m--)
    {
        ///C为合并,Q1为 检查是否在同一区间,Q2检查长度;
        char op[2];
        scanf("%s",&op);
        int a,b;
        if(op[0]=='C')
        {
            cin>>a>>b;
            if(find(a)==find(b)) continue;///特判
            size[find(b)]+=size[find(a)];
            p[find(a)]=find(b);
        }
        else if(op[1]=='1')
        {
            cin>>a>>b;
            if(find(a)==find(b))
                cout<<"YES"<<endl;
            else
                cout<<"No"<<endl;
        }
        else if(op[1]=='2')
        {
            cin>>a;
            cout<<size[find(a)]<<endl;
        }
    }
}
6-3 食物链
https://www.luogu.com.cn/problem/P2024
///这题是真难,看视频加理解一共花了俩小时
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,res=0;
int p[N],d[N];
int find(int x)
{
    if(p[x]!=x)///与以往不同,这里的d数组刚开始的作用是,x节点与父节点的距离,经过find函数递归之后
                    ///d数组变成了,x节点到头节点的距离,这样就可以来判断是不是同一类和接下来做题了
    {
        int t=find(p[x]);
        d[x]+=d[p[x]];///因为要用到p[x],所以先储存;
        p[x]=t;
    }
    return p[x];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        p[i]=i;
    while(m--)
    {
        int a,b,c;
        cin>>c>>a>>b;
        if(a>n||b>n) res++;
        else
        {
            if(c==1)
            {
                int pa=find (a),pb=find(b);
                if(pa==pb&&(d[a]-d[b])%3) res++;///3个一循环;如果A和B是同类,而且他俩已经在合并过了,那么两者的距离相减,如果余三不得0那么就是错的;
                else
                {
                    p[pa]=pb;
                    d[pa]=d[b]-d[a];
                }
            }
            else
            {
                int pa=find(a),pb=find(b);
                if(pa==pb&&(d[a]-d[b]-1)%3) res++;
                else if(pa!=pb)
                {
                    p[pa]=pb;
                    d[pa]=d[b]+1-d[a];
                }
            }
        }
    }
    cout<<res;
    return 0;
}
6-4 自动程序检测装置
https://www.luogu.com.cn/problem/P1955
///这个题是我得知有查并集这个算法的起初
///查并集+离散化,题目i和j都开到10的九次方了,不用离散化等着爆吧
///本题思路
#include<bits/stdc++.h>
using namespace std;
const int N=400010;
int t,n;
int a[2*N],p[N],d[2*N],b[N],c[N];
int find(int x)///
{
    if(p[x]!=x)
        p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[2*i-1]>>a[2*i]>>c[i];
            d[2*i-1]=a[2*i-1],d[2*i]=a[2*i];
        }
    sort(d+1,d+2*n+1);
    int h=0;
    b[0]=-1;
    for(int i=1;i<=2*n;i++)
        if(b[h]!=d[i]) b[++h]=d[i];///离散化完成,用了两次这个离散化模板了,感觉yxc老师的那个离散化模板用不了一点
    for(int i=1;i<=h;i++)
        p[i]=i;///更新爹节点
    for(int i=1;i<=n;i++)
    {
        if(c[i]==0)
            continue;
        int x=lower_bound(b+1,b+h+1,a[2*i-1])-b;
        int y=lower_bound(b+1,b+h+1,a[2*i])-b;
        if(find(x)==find(y)) continue;
        else
            p[find(x)]=find(y);///将所有相等的元素,放进树里面;接下来的就是狠狠的查找有没有内鬼
    }
    bool f=false;
    for(int i=1;i<=n;i++)
    {
        if(c[i]==1)
            continue;
        int x=lower_bound(b+1,b+h+1,a[2*i-1])-b;
        int y=lower_bound(b+1,b+h+1,a[2*i])-b;
        if(find(x)==find(y))///查找内鬼,如果有人在相等的树里面,就是内鬼!
        {
            cout<<"NO"<<endl;
            f=true;
            break;
        }
    }
    if(!f)
        cout<<"YES"<<endl;
}
    return 0;
}
6-6 A-bugs
http://bailian.openjudge.cn/practice/2492/
    ///此题考察并查集的应用,考察的是加权并查集,类似于食物链,需要考虑两者之间存在距离关系


///大题思路:每当输出两个数字是,代表这两个数字之间肯定是异性关系(如果两者满足距离为奇数)
///每当新的数加入到另一个树上时,如果符合条件,那么就意味这两者的距离之差至少为1,并且都是奇数,再向下拓展一位,也就是距离为偶数的,就是同性
///可以用da-db%2来判断,如果两者在一个树上并且满足这个条件,这两个人就是同性!

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,t;
int p[N],d[N];
int find(int x)
{
    if(x!=p[x])
    {
        int t=find(p[x]);
        d[x]+=d[p[x]];
        p[x]=t;
    }
    return p[x];
}
int main()
{
    cin>>t;
    int res=1;
    while(t--)
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            p[i]=i;
        memset(d,0,sizeof d);
        bool f=false;
        while(m--)
        {
            int a,b;
            cin>>a>>b;
            int pa=find(a),pb=find(b);
            if(pa==pb&&(d[a]-d[b])%2==0)
                f=true;
            else
            {
                p[pa]=p[b];
                d[pa]=d[b]+1-d[a];
            }
        }
        if(f)
        {
            printf("Scenario #%d:\n",res++);
            cout<<"Suspicious bugs found!"<<endl;
        }
        else
        {
            printf("Scenario #%d:\n",res++);
            cout<<"No suspicious bugs found!"<<endl;
        }
        cout<<endl;
    }
    return 0;
}

 

//https://www.luogu.com.cn/problem/P1525

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,res,p[N],vis[N];
struct node
{
    int a,b,c;
    bool operator<(const node&w) const
    {
        return c>w.c;
    }
}per[N];
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m;

    //根据题意理解,考虑贪心,根据库鲁斯卡尔,即最小生成树的思路
    //如果将边权从大到小排序,然后开始进行合并
    //考虑如何合并,首先如果两人已经在同一个监狱了,由于我们从大到小排序,所以这个一定是最大的,直接输出结束即可
    //如果没在,那么要进行一个判断,设两人为x和y,有 x不能和y在同一个监狱,z不能和x在同一个监狱,那么z会和y在一个监狱
    //所以如果此时x没有标记z的话,y就变成z,然后等待另一个y,将y和z合并,即 敌人的敌人是朋友
    //没有合并的不需要考虑了,因为我们一定是最大的

    for(int i=1;i<=n;i++) p[i]=i;
    for(int i=1;i<=m;i++) cin>>per[i].a>>per[i].b>>per[i].c;
    sort(per+1,per+1+m);
    for(int i=1;i<=m;i++){
        int x=per[i].a,y=per[i].b,w=per[i].c;
        if(find(x)==find(y)) return cout<<w,0;
        if(vis[x]==0) vis[x]=y; //如果此时x没有先前的敌对,那就先标记
        else p[find(y)]=find(vis[x]); //将y和x的敌对放到同一个监狱
        if(vis[y]==0) vis[y]=x;
        else p[find(x)]=find(vis[y]);
    }
    cout<<0; //如果都符合,输出0
    return 0;
}
//https://www.luogu.com.cn/problem/P1621

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,res,ans,num,k;
int prime[N],prime_[N],p[N];
bool vis[N];
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=n;i<=m;i++) p[i]=i;
    //预处理出来2-m的所有质数
    for(int i=2;i<=m;i++){
        if(!vis[i]) prime_[++res]=i;
        for(int j=1;prime_[j]<=m/i;j++){
            vis[prime_[j]*i]=true;
            if(!(i%prime_[j])) break;
        }
    }
    //预处理k-m的质数
    for(int i=1;i<=res;i++) if(prime_[i]>=k) prime[++num]=prime_[i];
    //对于每个质数,算出对应的这个数的最小倍数,然后遍历这个质数对应最小的质数到最大数的区间即可
    for(int i=1;i<=num;i++){
        int x=(n+prime[i]-1)/prime[i]*prime[i];
        for(int j=x;j<=m;j+=prime[i]) p[find(j)]=find(x);
    }
    //统计集合数量,即根节点还是本身的数量
    for(int i=n;i<=m;i++) if(p[i]==i) ans++;
    cout<<ans;
    return 0;
}

 带权并查集:

//https://www.luogu.com.cn/problem/P1196
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,mod=1e9+7;
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n; cin>>n;
    vector<int>p(N+1),sz(N+1),dep(N+1);
    for(int i=1;i<=N;i++) p[i]=i,sz[i]=1;
    function<int(int)>find;
    find=[&](int x)->int{
        if(x==p[x]) return p[x];
        int root=find(p[x]);
        dep[x]+=dep[p[x]];
        return p[x]=root;
    };
    while(n--){
        char op; 
        int a,b; cin>>op>>a>>b;
        if(op=='M'){
            int u=find(a),v=find(b);
            if(u==v) continue;
            p[u]=v,dep[u]=sz[v],sz[v]+=sz[u];
        }
        else{
            if(find(a)!=find(b)) cout<<-1<<'\n';
            else cout<<abs(dep[a]-dep[b])-1<<'\n';
        }
    }
    return 0;
}

 

posted @ 2023-05-24 13:02  o-Sakurajimamai-o  阅读(18)  评论(0)    收藏  举报
-- --