看到就猜是把边权从小到大排序然后插件去。。然而事实上排序后某条边必须插就插这条边是对的!
考虑一条边(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