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