bzoj 3559: [Ctsc2014]图的分割 并查集

        看到就猜是把边权从小到大排序然后插件去。。然而事实上排序后某条边必须插就插这条边是对的!

       考虑一条边(x,y,z),x所在集合为u,y所在集合为v,其中u!=v;那么如果z<=min(M(u)+Z[|u|],M(v)+Z[|v|]),就把u和v并起来;否则不做改动。显然如果某一次不作改动那么u和v就永远不会变化了,因此得到的是半完美的,或者就会把所有的并到一起。

       显然这样也是完美的,因为对于一个集合u,如果可以分类为p和q,那么连接p和q之间的边是不会连的,这样就矛盾了。因此必然完美。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;

int n,m,cnt,w[N],fa[N],sz[N],val[N],fst[N],nxt[N];
struct node{ int x,y,z; }a[N*5];
int read(){
    int x=0; char ch=getchar();  
    while (ch<'0' || ch>'9') ch=getchar();  
    while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }  
    return x;  
}
bool cmp(const node &u,const node &v){ return u.z<v.z; }
int getfa(int x){ return (x==fa[x])?x:fa[x]=getfa(fa[x]); }
void add(int x,int y){ if (x==y) cnt++; nxt[y]=fst[x]; fst[x]=y; }
int main(){
	n=read(); m=read(); int i,j,u,v;
	for (i=1; i<=n; i++){
		w[i]=read();
		fa[i]=i; sz[i]=1; val[i]=w[1];
	}
	for (i=1; i<=m; i++){
		a[i].x=read(); a[i].y=read(); a[i].z=read();
	}
	sort(a+1,a+m+1,cmp);
	for (i=1; i<=m; i++){
		u=getfa(a[i].x); v=getfa(a[i].y);
		if (u!=v && a[i].z<=min(val[u],val[v])){
			if (sz[u]>sz[v]) swap(u,v);
			fa[u]=v; sz[v]+=sz[u]; val[v]=w[sz[v]]+a[i].z;
		}
	}
	for (i=n; i; i--) add(getfa(i),i);
	printf("%d\n",cnt);
	for (i=1; i<=n; i++) if (fa[i]==i){
		printf("%d",sz[i]);
		for (j=fst[i]; j; j=nxt[j]) printf(" %d",j);
		puts("");
	}
	return 0;
}


by lych

2016.5.27

你可能感兴趣的:(并查集)