并查集

并查集

关键操作

1.预处理

for(int i=1;i<=m;i++) fa[i]=i;

2.路径压缩O(n)

int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
3.merge
int fx=find(a[i].x),fy=find(a[i].y);    
fa[fx]=fy;

3.按秩合并O(nlogn)

秩的意思就是树的高度,按秩合并过后并查集的结构为树形结构

程序自动分析

第一步当然是先离散化

然后对于“=”操作,我们直接merge

对于“≠”操作,我们check一下他们如果在一个联通块中就 return no,否则不动

然后小trick,我们可以按 e 排个序,让 “=” 操作先执行

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}

int T,n;
int fa[1000005];
struct node{
    long long x,y;int z;
    bool operator < (const node &x) const {
        return z>x.z;
    }
}a[1000005];
long long b[2000050];
int m;
int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool flag;
int main() {
    T=read();
    while(T--) {
        n=read();m=0;flag=0;
        for(int i=1,x,y,z;i<=n;i++) {
            scanf("%lld%lld",&a[i].x,&a[i].y);a[i].z=read();
            b[++m]=a[i].x;b[++m]=a[i].y;
        }
        sort(b+1,b+1+m);
        m=unique(b+1,b+1+m)-b-1;
        for(int i=1;i<=n;i++) {
            a[i].x=lower_bound(b+1,b+1+m,a[i].x)-b;
            a[i].y=lower_bound(b+1,b+1+m,a[i].y)-b;
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=m;i++) fa[i]=i;
        for(int i=1;i<=n;i++) {
            int fx=find(a[i].x),fy=find(a[i].y);
            if(a[i].z==1) {
                fa[fx]=fy;
            } else {
                if(fa[fx]==fa[fy]) {
                    flag=1;break;
                }
            }
        }
        if(flag) puts("NO");
        else puts("YES");
    }
    return 0;
}

supermarket

solution

简而言之,就是维护一个时间并查集,一开始的时间都是截止时间卖出,然后按利润从大到小排序(贪心),卖了这个就把其余的这天卖出的和前一天卖出的合并,然后每次 x=find(a[i].d)找到的都是能卖出的最靠后的天数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int n,mxtime;
int fa[10005];
struct node{
    int p,d;
    bool operator < (const node &x) const {
        return p>x.p;
    }
}a[10005];
int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int ans;
int main() {
    while(scanf("%d",&n)!=EOF) {
        ans=0;mxtime=0;
        for(int i=1;i<=n;i++) {
            scanf("%d%d",&a[i].p,&a[i].d);
            mxtime=max(mxtime,a[i].d);
        }
        for(int i=1;i<=mxtime;i++)
            fa[i]=i;
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++) {
            int x=find(a[i].d);
            if(x>0) {
                ans+=a[i].p;
                fa[x]=x-1;
            }
        }
        printf("%d\n",ans); 
    }
    return 0;
}

[NOI2002]银河英雄传说

边带权并查集

维护siz和 d[]表示当前这个点到 fa[x] 的距离

然后每次合并注意先不要直接直接路径压缩fa

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n;
int fa[30005],d[30005],siz[30005];
int find(int x) {
    if(x==fa[x]) return x;
    int ans=find(fa[x]);
    d[x]+=d[fa[x]];
    return fa[x]=ans;
}
void merge(int x,int y) {
    fa[x]=y;
    d[x]+=siz[y];
    siz[y]+=siz[x];
    siz[x]=0;
}
int abs(int op) {
	return op<0?-op:op;
}
int x,y;
char c;
int main() {
    n=read();
    for(int i=1;i<=30000;i++) fa[i]=i,siz[i]=1;
    for(int i=1;i<=n;i++) {
        cin>>c;
        x=read();y=read();
        int fx=find(x),fy=find(y);
        if(c=='M') merge(fx,fy);
        else {
            if(fx!=fy) puts("-1");
            else printf("%d\n",abs(d[y]-d[x])-1);
        }
    }
    return 0;
}	

Parity game

解答见蓝书p198

边带权并查集

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
const int N=40010;
int n,m;
struct node{
    int l,r;int val;
}q[N];
int b[N],len;
int fa[N],d[N];
char c[5];
int find(int x) {
    if(x==fa[x]) return x;
    int ans=find(fa[x]);
    d[x]^=d[fa[x]];
    return fa[x]=ans;
}
int main() {
    n=read();m=read();
    for(int i=1;i<=m;i++) {
        q[i].l=read();q[i].r=read();scanf("%s",c);
        q[i].val=(c[0]=='o'?1:0);
        b[++len]=q[i].l-1; b[++len]=q[i].r;
    }
    sort(b+1,b+1+len);
    n=unique(b+1,b+1+len)-b-1;

    for(int i=1;i<=2*n;i++) fa[i]=i;
    for(int i=1;i<=m;i++) {
        int x=lower_bound(b+1,b+1+n,q[i].l-1)-b-1;
        int y=lower_bound(b+1,b+1+n,q[i].r)-b-1;
        int fx=find(x),fy=find(y);
        if(fx==fy) {
             if((d[x]^d[y])!=q[i].val) {
                 printf("%d\n",i-1);
                 return 0;
             }
        } else {
            fa[fx]=fy;
            d[fx]=d[x]^d[y]^q[i].val;
        }
    }
    printf("%d\n",m);
    return 0;
}

扩展域并查集

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
const int N=40010;
int n,m;
struct node{
    int l,r;int val;
}q[N];
int b[N],len;
int fa[N],d[N];
char c[5];
int find(int x) {
    if(x==fa[x]) return x;
    int ans=find(fa[x]);
    d[x]^=d[fa[x]];
    return fa[x]=ans;
}
int main() {
    n=read();m=read();
    for(int i=1;i<=m;i++) {
        q[i].l=read();q[i].r=read();scanf("%s",c);
        q[i].val=(c[0]=='o'?1:0);
        b[++len]=q[i].l-1; b[++len]=q[i].r;
    }
    sort(b+1,b+1+len);
    n=unique(b+1,b+1+len)-b-1;

    for(int i=1;i<=2*n;i++) fa[i]=i;
    for(int i=1;i<=m;i++) {
        int x=lower_bound(b+1,b+1+n,q[i].l-1)-b;
        int y=lower_bound(b+1,b+1+n,q[i].r)-b;
        int x_odd=x,x_even=x+n;
        int y_odd=y,y_even=y+n;
        if(q[i].val==0) {
            if(find(x_odd)==find(y_even)) {
                printf("%d\n",i-1);
                return 0;
            }
            fa[find(x_odd)]=find(y_odd);
            fa[find(x_even)]=find(y_even);
        } else {
            if(find(x_odd)==find(y_odd)) {
                printf("%d\n",i-1);
                return 0;
            }
            fa[find(x_odd)]=find(y_even);
            fa[find(x_even)]=find(y_odd);
        }
    }
    printf("%d\n",m);
    return 0;
}

食物链

扩展域并查集

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
const int N=150010;
int n,m,ans;
int fa[N];
char c[5];
int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main() {
    n=read();m=read();

    for(int i=1;i<=3*n;i++) fa[i]=i;

    for(int i=1;i<=m;i++) {
        int val=read(),x=read(),y=read();
        if(x>n||y>n) {
            ans++;
            continue;
        }
        if(x==y&&val==2) {
            ans++;
            continue;
        }
        int x_self=x,x_enemy=x+n,x_eat=x+2*n;
        int y_self=y,y_enemy=y+n,y_eat=y+2*n;
        if(val==1) {
            if(find(x_eat)==find(y_self) || find(y_eat)==find(x_self)) {
                ans++;
                continue;
            }
            fa[find(x_eat)]=find(y_eat);
            fa[find(x_enemy)]=find(y_enemy);
            fa[find(x_self)]=find(y_self);
        } else {
            if(find(y_eat)==find(x_self) || find(x_self)==find(y_self)) {
                ans++;
                continue;
            }
            fa[find(x_eat)]=find(y_self);
            fa[find(x_self)]=find(y_enemy);
            fa[find(x_enemy)]=find(y_eat);            
        }
    }
    printf("%d\n",ans);
    return 0;
}

关押罪犯

排序,我们要尽可能让危害大的罪犯在两个监狱里。

那么,再结合敌人的敌人和自己在一个监狱的规律合并。

当查找时发现其中两个罪犯不可避免地碰撞到一起时,只能将其输出并结束。

还有一点很重要,就是没有冲突时一定输出0!!!

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,m;
int fa[100005],enemy[100005];
struct node{
    int x,y,z;
    bool operator < (const node &x) const {
        return z>x.z;
    }
}a[100005];
int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void merge(int x,int y) {
    x=find(x),y=find(y);
    fa[x]=y;
}
int main() {
    n=read();m=read();
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++) {
        a[i].x=read();a[i].y=read();a[i].z=read();
    }
    sort(a+1,a+1+m);
    for(int i=1;i<=m;i++) {
        if(find(a[i].x)==find(a[i].y)) {printf("%d\n",a[i].z);return 0;}
        if(!enemy[a[i].x]) enemy[a[i].x]=a[i].y;
        else merge(enemy[a[i].x],a[i].y);
        if(!enemy[a[i].y]) enemy[a[i].y]=a[i].x;
        else merge(enemy[a[i].y],a[i].x);
    }
    puts("0");
    return 0;
}

CF85E Guard Towers

排个序,从大到小

和上题类似,连自己和对方的敌人,直到两人或敌人的find相同,这是ans=i ;

然后再求连通块数目得到方案数

#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
#define pii pair<int,int> 
#define MP make_pair
using namespace std;
const int N=100005;
const int P=1e9+7;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}

int n,x[N],y[N];
long long ans;
vector< pii >v[N];
int fa[N];
inline int find(int x) {
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline int jue(int x) {return x>0?x:(-x);}
inline int dist(int i,int j) {
	return jue(x[i]-x[j])+jue(y[i]-y[j]);
}
int main() {
	n=read();
	for(int i=1;i<=n;i++) {
		x[i]=read();y[i]=read();
		for(int j=1;j<i;j++) 
			v[dist(i,j)].push_back(MP(i,j));
	}
	for(int i=1;i<=2*n;i++) fa[i]=i;
	for(int i=10000;i>=0;i--) {
		for(auto t: v[i]) {
			int x=find(t.first),y=find(t.second);
			int ex=find(t.first+n),ey=find(t.second+n);
			if(x==y||ex==ey) {
				ans=i;break;
			}
			fa[x]=ey;fa[y]=ex;
		}
		if(ans) break;
	}
	printf("%lld\n",ans);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=ans+1;i<=10000;i++) {
		for(auto t: v[i]) {
			int x=find(t.first),y=find(t.second);
			fa[x]=y;
		}
	}
	ans=1;
	for(int i=1;i<=n;i++) 
		if(fa[i]==i)
			ans=ans*2%P;
	printf("%lld\n",ans);
	return 0;
}


UVA 11354 - Bond

#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=2e5+5;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
struct Edge{
	int u,v,w;
	bool operator < (const Edge &x) const {
		return w<x.w;
	}
}e[N];
int hd[N],tot;
int n,m,fa[N],siz[N],val[N];
int find(int x) {
	return x==fa[x]?x:find(fa[x]);
}
void merge(int x,int y,int z) {
	x=find(x),y=find(y);
	if(x==y) return;
	if(siz[x]<siz[y]) swap(x,y);
	fa[y]=x;val[y]=z;
	if(siz[x]==siz[y]) siz[x]++;
}
int v[N];
int query(int x,int y) {
	for(int i=0;i<=n;i++) v[i]=0;
	int ans=1,ans1=0;
	while(1) {
		v[x]=ans;
		if(x==fa[x]) break;
		ans=max(ans,val[x]);
		x=fa[x];
	}
	while(1) {
		if(v[y]) {ans1=max(ans1,v[y]);break;}
		if(y==fa[y]) break;
		ans1=max(ans1,val[y]);
		y=fa[y];
	}
	return ans1;
}
int cs;
int main() {
	while(scanf("%d%d",&n,&m)==2) {
		if(cs++) putchar('\n');
		for(int i=0;i<=n;i++) fa[i]=i,siz[i]=0,val[i]=0;
		for(int i=1;i<=m;i++) {
			e[i].u=read(),e[i].v=read(),e[i].w=read();
		}	
		sort(e+1,e+1+m);
		for(int i=1;i<=m;i++) 
			merge(e[i].u,e[i].v,e[i].w);
		int q;	
		q=read();
		int x,y;
		while(q--) {
			x=read(),y=read();
			printf("%d\n",query(x,y));
		}
	}
	return 0;
}


货车运输

#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=2e5+5;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
struct Edge{
	int u,v,w;
	bool operator < (const Edge &x) const {
		return w>x.w;
	}
}e[N];
int hd[N],tot;
int n,m,fa[N],siz[N],val[N],dep[N];
int find(int x) {
	if(x==fa[x]) return val[x]=0,x;
	int dad=find(fa[x]);
	dep[x]=dep[fa[x]]+1;
	return dad;
}
void merge(int x,int y,int z) {
	x=find(x),y=find(y);
	if(x==y) return;
	if(siz[x]<siz[y]) swap(x,y);
	fa[y]=x;val[y]=z;
	if(siz[x]==siz[y]) siz[x]++;
}
int query(int x,int y) {
	if(find(x)!=find(y)) return -1;
	int ans=0x3f3f3f3f;
	while(x!=y) {
		if(dep[x]<dep[y]) swap(x,y);
		ans=min(ans,val[x]);
		x=fa[x];
	}
	return ans;
}
int cs;
int main() {
	n=read();m=read();
	for(int i=0;i<=n;i++) fa[i]=i,siz[i]=0,val[i]=0;
	for(int i=1;i<=m;i++) {
		e[i].u=read(),e[i].v=read(),e[i].w=read();
	}	
	sort(e+1,e+1+m);
	for(int i=1;i<=m;i++) 
		merge(e[i].u,e[i].v,e[i].w);
	int q;	
	q=read();
	int x,y;
	while(q--) {
		x=read(),y=read();
		printf("%d\n",query(x,y));
	}
	return 0;
}

城市猎人

提交 http://192.168.14.142/problem/57

对于第i天 ,设x=m-i+1 ,则会有 $$ x1,x2...x*j $$这些边相连,且边权为 i ,如果做过按秩合并的题,那么可一眼看出。。。接下来就是按秩合并咯

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=100010;
inline int read() {
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x;
}
int n,m,q;
int fa[N],siz[N],dep[N];
int val[N];
inline int find(int x) {
	if(fa[x]==x) return dep[x]=0,x;
	int dad=find(fa[x]);
	dep[x]=dep[fa[x]]+1;
	return dad;
}
void merge(int x,int y,int z) {
	x=find(x),y=find(y);
	if(x!=y) {
		if(siz[x]<siz[y]) swap(x,y);
		fa[y]=x;val[y]=z;
		if(siz[x]==siz[y])  siz[x]++;		
	}
}
int main() {
	n=read();m=read();q=read();
	for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
	for(int i=1;i<=m;i++) {
		int x=m-i+1;
		for(int j=1;x*j+x<=n;j++) merge(x*j,x*j+x,i);
	}
	while(q--) {
		int x=read(),y=read(),ans=0;
		find(x),find(y);
		if(dep[x]<dep[y]) swap(x,y);
		while(dep[x]>dep[y]) ans=max(ans,val[x]),x=fa[x];
		while(x!=y) {
			ans=max(ans,val[x]);x=fa[x];
			ans=max(ans,val[y]);y=fa[y];
		}
		printf("%d\n",ans);
	} 
	
	return 0;
}


冷战

posted @ 2020-09-01 07:22  ke_xin  阅读(43)  评论(0编辑  收藏  举报