并查集(专题)

解题报告:看似像博弈论的问题,其实就是在询问当两个点在一个集合了就结束游戏了,并查集处理就行了。

#include<iostream>
using namespace std;
const   int N=210,M=N*N;
int p[M];
int n,m;
int get(int x,int y)
{
   
    return x*n+y;
}
int find(int x)
{
   
    if(p[x]!=x)
    p[x]=find(p[x]);
    return p[x];
}
int main()
{
   
    cin>>n>>m;
    for(int i=0;i<M;i++)
    p[i]=i;
    int res=0;
    for(int i=1;i<=m;i++)
    {
   
        int x,y;
        char str[2];
        cin>>x>>y;
        cin>>str;
        x--;
        y--;
        int t=get(x,y);
        int t2;
        if(str[0]=='D')
        {
   
             t2=get(x+1,y);
        }
        else
        {
   
            t2=get(x,y+1);
            
        }
            if(find(t2)==find(t))
            {
   
                res=i;
                break;
            }
            else
            {
   
                p[find(t2)]=find(t);
            }
    }
    if(!res)    puts("draw");
    else        cout<<res<<endl;
    return 0;
}

解题报告:这种题其实就可以把一个集合的物品一起打包,然后做一遍01背包就行了。

#include<iostream>
#include<cstring>
using namespace std;
const   int N=10010;
int p[N];
int v[N];
int w[N];
int f[N];
int find(int x)
{
   
    if(x!=p[x])
    p[x]=find(p[x]);
    return p[x];
}
int main()
{
   
    int n,m,W;
    cin>>n>>m>>W;
    for(int i=1;i<=n;i++)
    p[i]=i;
    for(int i=1;i<=n;i++)
    {
   
        int a,b;
        cin>>a>>b;
        v[i]=b,w[i]=a;
    }
    for(int i=1;i<=m;i++)
    {
   
        int a,b;
        cin>>a>>b;
        int pa=find(a),pb=find(b);
        if(pa!=pb)
        {
   
            p[pb]=pa;
            v[pa]+=v[pb];
            w[pa]+=w[pb];
        }
    }
    for(int i=1;i<=n;i++)
    if(p[i]==i)
    for(int j=W;j>=w[i];j--)
    f[j]=max(f[j],f[j-w[i]]+v[i]);
    cout<<f[W]<<endl;
    return 0;
}


解题报告:这种题先做一遍离散化,因为这些数1~1e9太大了,用hash来做,手写hash哦。

#include<iostream>
#include<cstring>
using namespace std;
const   int N=2000003;
int p[N];
int h[N],e[N],ne[N];
int idx;
int find(int x)
{
   
    if(x!=p[x])
    p[x]=find(p[x]);
    return p[x];
}
struct node{
   
    int a;
    int b;
    int c;
}edge[N];
void insert(int x)
{
   
    int t=(x%N+N)%N;
    e[idx]=x,ne[idx]=h[t],h[t]=idx++;
}
bool find1(int x)
{
   
    int t=(x%N+N)%N;
    for(int i=h[t];~i;i=ne[i])
    {
   
        int j=e[i];
        if(j==x)
        return true;
    }
    return false;
}
int hash1(int x)
{
   
    int t=(x%N+N)%N;
    for(int i=h[t];~i;i=ne[i])
    {
   
        if(e[i]==x)
        return i;
    }
}
int main()
{
   
    int T;
    cin>>T;
    while(T--)
    {
   
        memset(h,-1,sizeof h);
        idx=0;
        int cnt=0;
        int n;
        for(int i=0;i<=N-1;i++)
        p[i]=i;
        cin>>n;
        for(int i=0;i<n;i++)
        {
   
            int a,b,c;
            cin>>a>>b>>c;
            if(!find1(a))
            insert(a);
            if(!find1(b))
            insert(b);
            edge[i]={
   hash1(a),hash1(b),c};
            if(c==1)
            {
   
                int pa=find(hash1(a));
                int pb=find(hash1(b));
                p[pa]=pb;
            }
        }
        bool flag=true;
        for(int i=0;i<n;i++)
        {
   
            if(edge[i].c==0)
            {
   
                int pa=find(edge[i].a);
                int pb=find(edge[i].b);
                if(pa==pb)
                {
   
                    flag=false;
                    break;
                }
            }
        }
        
        if(!flag)   puts("NO");
        else        puts("YES");
    }
    return 0;
}

解题报告:这题就要想一想了,告诉你l和r,这个区间的和是否为偶,我们可以依据这个来判断s[r]和s[l-1]之间的奇偶相对性,然后用带权并查集或者扩展域并查集做就行了。
带权并查集:

#include<iostream>
#include<cstring>
#include<unordered_map>
using namespace std;
const   int N=20010;
int p[N];
int d[N];
unordered_map<long long,int>S;
int cnt;
int find(int x)
{
   
    if(x!=p[x])
    {
   
        int root=find(p[x]);
        d[x]=(d[x]+d[p[x]])%2;
        p[x]=root;
    }
    return p[x];
}
int get_id(int x)
{
   
    if(!S.count(x))
    S[x]=++cnt;
    return S[x];
}
int main()
{
   
    int n,m;
    cin>>n>>m;
    for(int i=1;i<N;i++)
    p[i]=i;
    int res=m;
    for(int i=1;i<=m;i++)
    {
   
        int a,b;
        int type =0;
        string t;
        cin>>a>>b>>t;
        a=get_id(a-1);
        b=get_id(b);
        int pa=find(a);
        int pb=find(b);
        if(t=="odd") type = 1;
        if(pa!=pb)
        {
   
            d[pa]=(d[b]-d[a]+type)%2;
            p[pa]=pb;
        }
        else
        {
   
            if(((d[a]-d[b])%2+2)%2!=type)
            {
   
                res=i-1;
                break;
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

扩展域:

#include<iostream>
#include<cstring>
#include<unordered_map>
using namespace std;
const   int N=40010,base=N/2;
int p[N];
unordered_map<int,int>S;
int cnt;
int find(int x)
{
   
    if(x!=p[x])
    p[x]=find(p[x]);
    return p[x];
}
int get_id(int x)
{
   
    if(!S.count(x))
    S[x]=++cnt;
    return S[x];
}
int main()
{
   
    int n,m;
    cin>>n>>m;
    n=0;
    for(int i=1;i<N;i++)
    p[i]=i;
    int res=m;
    for(int i=1;i<=m;i++)
    {
   
        int a,b;
        int type =0;
        string t;
        cin>>a>>b>>t;
        a=get_id(a-1);
        b=get_id(b);
        if(t=="odd") type = 1;
        if(type)
        {
   
            if(find(a)==find(b))
            {
   
                res=i-1;
                break;
            }
            p[find(a)]=find(b+base);
            p[find(a+base)]=find(b);
        }
        else
        {
   
            if(find(a+base)==find(b))
            {
   
                res=i-1;
                break;
            }
            p[find(a)]=find(b);
            p[find(a+base)]=find(b+base);
        }
    }
    cout<<res<<endl;
    return 0;
}

解题报告:维护一个d数组,d代表到根节点的距离,每次查询根节点之间距离就是abs(d[a]-d[b])-1,如果a和b同一个点那就输出0就行了。每次合并的时候让合并的点加上前面船的数量。

#include<bits/stdc++.h>
using namespace std;
const   int N=30010;
int d[N],p[N],Size[N];
int find(int x)
{
   
    if(x!=p[x])
    {
   
        int root=find(p[x]);
        d[x]+=d[p[x]];
        p[x]=root;
    }
    return p[x];
}
int main()
{
   
    int m;
    cin>>m;
    for(int i=1;i<N;i++)
    p[i]=i,Size[i]=1;
    while(m--)
    {
   
        char op[2];
        int a,b;
        cin>>op>>a>>b;
     // cout<<op<<endl; 
     int pa=find(a);
            int pb=find(b);
        if(op[0]=='M')
        {
   
         // cout<<a<<' '<<b<<endl;
       
            p[pa]=pb;
            d[pa]=Size[pb];
            Size[pb]+=Size[pa];
        }
        else
        {
   
           // cout<<pa<<' '<<pb<<endl;
            if(pa!=pb)
            puts("-1");
            else
            {
   
                cout<<max(0,abs(d[a]-d[b])-1)<<endl;
            }
        }
    }
    return 0;
}


扩展域写法:

#include<iostream>
#include<cstring>
using namespace std;
const   int N=150010;
int p[N];
int find(int x)
{
   
    if(p[x]!=x)
    p[x]=find(p[x]);
    return p[x];
}
int main()
{
   
    int n,k;
    cin>>n>>k;
    for(int i=0;i<N;i++)
    p[i]=i;
    int res=0;
    while(k--)
    {
   
        int op,a,b;
        cin>>op>>a>>b;
        if(op==2&&a==b)    
        {
   
            res++;
            continue;
        }
        if(a>n||b>n)
        {
   
            res++;
            continue;
        }
        if(op==1)
        {
   
            int pa=find(a),pb=find(b);
            if(find(a)==find(b+n)||find(a+n)==find(b)) //a是b的天敌,或者b是a的天敌
            {
   
                res++;
                continue;
            }
            p[find(a)]=find(b);
            p[find(a+n)]=find(b+n);
            p[find(a+2*n)]=find(b+2*n);
        }
        else
        {
   
        if(find(a)==find(b)||find(b)==find(a+n)) //b是a的天敌,或者a和b是同类
            {
   
                res++;
                continue;
            }
            p[find(a)]=find(b+n);
            p[find(a+n)]=find(b+2*n);
            p[find(a+2*n)]=find(b);
        }
    }
    cout<<res<<endl;
    return 0;
}
全部评论

相关推荐

10-28 11:04
已编辑
美团_后端实习生(实习员工)
一个2人:我说几个点吧,你的实习经历写的让人觉得毫无含金量,你没有挖掘你需求里的 亮点, 让人觉得你不仅打杂还摆烂。然后你的简历太长了🤣你这个实习经历看完,估计没几个人愿意接着看下去, sdk, 索引这种东西单拎出来说太顶真了兄弟,好好优化下简历吧
点赞 评论 收藏
分享
牛客279957775号:铁暗恋
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务