并查集的应用

  重学并查集,该会的一些操作还是得会。。。

1.路径压缩

2.按秩合并

都略了,一道亲戚两者都用,代码:

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
#include<deque>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
const int maxn=5002;
int n,m,k;
int f[maxn],r[maxn];//按秩合并
int getfather(int x)
{
    if(x==f[x])return x;
    return f[x]=getfather(f[x]);
}
void merge(int x,int y)
{
    if(r[x]>=r[y])f[y]=x,r[x]=max(r[x],r[y]+1);
    else f[x]=y,r[y]=max(r[x]+1,r[y]);
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();k=read();
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        x=read();y=read();
        int xx=getfather(x);
        int yy=getfather(y);
        merge(xx,yy);
    }
    for(int i=1;i<=k;i++)
    {
        int x,y;
        x=read();y=read();
        int xx=getfather(x);
        int yy=getfather(y);
        if(xx!=yy)printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}

听说这样每次getfather都是常数级别的,炒鸡快诶!

然后带边权的并查集,银河英雄传大家都听说过,还是不太好相同的代码丢一波。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<set>
#include<map>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=500002;
int n;
int f[maxn],d[maxn],sz[maxn];
char ch;
int getfather(int x)
{
    if(x==f[x])return x;
    int root=getfather(f[x]);
    d[x]+=d[f[x]];
    return f[x]=root;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)f[i]=i,sz[i]=1;
    for(int i=1;i<=n;i++)
    {
        int x,y;
        cin>>ch;
        x=read();y=read();
        if(ch=='M')
        {
            int xx=getfather(x);
            int yy=getfather(y);
            d[xx]=sz[yy];f[xx]=yy;
            sz[yy]+=sz[xx];
        }
        else 
        {
            int xx=getfather(x);
            int yy=getfather(y);
            if(xx!=yy)printf("-1\n");
            else printf("%d\n",abs(d[x]-d[y])-1);
        }
    }
    return 0;
}
View Code

例题 party game poj 1733 好题

很值得思考的一道题,放到这里啦。。

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
#include<deque>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<stack>
//#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    int num=0;char ch[500];
    while(x)ch[++num]=x%10+'0',x/=10;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
const int maxn=10002;
int n,m;
int a[maxn<<1],c[maxn<<1],top=0,k=0;
char b[10];
struct wy//闻道玉门犹被遮,应将性命逐轻车
{    
    int l,r,h;
}t[maxn<<1];
int f[maxn],d[maxn];//采用带边权的并查集边权为0则x与f[x]奇偶性相同,边权为1则相反
int getfather(int x)
{
    if(x==f[x])return x;
    int root=getfather(f[x]);
    d[x]=d[x]^d[f[x]];
    return f[x]=root;
// }
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        t[i].l=read()-1;
        t[i].r=read();
        scanf("%s",b+1);
        t[i].h=(b[1]=='e'?0:1);
        a[++top]=t[i].l;
        a[++top]=t[i].r;
    }
    //如果l~r之间区间1为偶数则l-1和r奇偶性相同,为奇数时奇偶性不同.
    sort(a+1,a+1+top);
    for(int i=1;i<=top;i++)if(i==1||a[i]!=a[i-1])c[++k]=a[i];//离散
    for(int i=1;i<=top;i++)f[i]=i;
    memset(d,0,sizeof(d));
    for(int i=1;i<=m;i++)
    {
        int x=lower_bound(c+1,c+1+k,t[i].l)-c;
        int y=lower_bound(c+1,c+1+k,t[i].r)-c;
        int xx=getfather(x);int yy=getfather(y);
        if(xx==yy){if((d[x]^d[y])!=t[i].h){put(i-1);return 0;}}
        else 
        {
            f[xx]=yy;d[xx]=d[x]^d[y]^t[i].h;
        }
    }
    put(m);
    return 0;
}
View Code

当然还可以用并查集的扩展域写。

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
#include<deque>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<stack>
//#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    int num=0;char ch[500];
    while(x)ch[++num]=x%10+'0',x/=10;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
const int maxn=10000;
int n,m;
int a[(maxn<<1)+2],c[(maxn<<1)+2],top=0,k=0;
char b[10];
struct wy//闻道玉门犹被遮,应将性命逐轻车
{    
    int l,r,h;
}t[(maxn<<1)+2];
int f[(maxn<<1)+2];//采用扩展域并查集
int getfather(int x)
{
    if(x==f[x])return x;
    return f[x]=getfather(f[x]);
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        t[i].l=read()-1;
        t[i].r=read();
        scanf("%s",b+1);
        a[++top]=t[i].l;
        a[++top]=t[i].r;
        t[i].h=(b[1]=='o')?1:0;
    }
    sort(a+1,a+1+top);
    for(int i=1;i<=top;i++)
        if(i==1||a[i]!=a[i-1])c[++k]=a[i];
    for(int i=1;i<=(maxn<<1);i++)f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x=lower_bound(c+1,c+1+k,t[i].l)-c;
        int y=lower_bound(c+1,c+1+k,t[i].r)-c;//代表奇数
        int xx=x+maxn;int yy=y+maxn;//代表偶数的
        if(t[i].h==0)
        {
            int u=getfather(x);
            int u1=getfather(yy);
            if(u==u1){put(i-1);return 0;}
            f[getfather(x)]=getfather(y);
            f[getfather(xx)]=getfather(yy);
        }
        else 
        {
            int u=getfather(x);
            int u1=getfather(y);
            if(u==u1){put(i-1);return 0;}
            f[getfather(x)]=getfather(yy);
            f[getfather(y)]=getfather(xx);
        }
    }
    put(m);
    return 0;
}
View Code

并查集的扩展域还有食物链等.

关键是第一条命令不好判断,是这样的如果是1的话x要吃的和y在一起或x本身和y要吃的就矛盾了

2的话是x和y在一起或x和y要吃的在一起就矛盾了,两条即可判断了。

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<queue>
#include<deque>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<bits/stdc++.h>
#define inf 50000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    int num=0;char ch[500];
    while(x)ch[++num]=x%10+'0',x/=10;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
const int maxn=50002;
int n,m;
int f[maxn<<2];
int ans=0;
int getfather(int x)
{
    if(x==f[x])return x;
    return f[x]=getfather(f[x]);
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=inf*3;i++)f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int p,x,y;
        p=read();x=read();y=read();//cout<<p<<endl;
        if(x>n||y>n){ans++;continue;}
        //x,y同类,xx,yy是捕食的,xxx,yyy是天敌
        int xx=x+inf;int yy=y+inf;
        int xxx=xx+inf;int yyy=yy+inf;
        if(p==1)
        {
            if(x==y){continue;}
            if(getfather(x)==getfather(yy)||getfather(xx)==getfather(y)){ans++;continue;}
            f[getfather(x)]=getfather(y);
            f[getfather(xx)]=getfather(yy);
            f[getfather(xxx)]=getfather(yyy);
        }
        if(p==2)//x吃y
        {
            if(x==y){ans++;continue;}
            if(getfather(x)==getfather(y)||getfather(x)==getfather(yy)){ans++;continue;}
            f[getfather(x)]=getfather(yyy);
            f[getfather(xx)]=getfather(y);
            f[getfather(xxx)]=getfather(yy);
        }
    }
    put(ans);
    return 0;
}
View Code

↖(^ω^)↗

posted @ 2018-12-14 18:42  chdy  阅读(285)  评论(0编辑  收藏  举报