陈题, 有篇论文, 利用了N多生成树的性质,大体思想就是通过调整某种边的权值后生成一个最小生成树, 这个新树所分的结构是与原树2种边按此分法生成的最小生成树是最优的, 这样就转换成了一种枚举一种边增加的权值后的生成树的算法, 而且我们要找的是一个区间
http://oj.tsinsen.com/resources/Train2012-test-clj-tree.pdf
#include <cstdio> #include <algorithm> using namespace std; const int M=100000+123; const int N=50000+123; struct Edge{ int u, v, w; }B[M], W[M]; int cntB, cntW; int p[N], rank[N]; int find(int x) { return (p[x]==x?x:p[x]=find(p[x])); } void init(int x) { for (int i=0; i<x; ++i) p[i]=i, rank[i]=1; } int ans; int kruskal(int x, int n)/// 同权值先加黑边 { init(n); int pb=0, pw=0; int res=0; while (pb<cntB || pw<cntW) { //printf("pb=%d pw=%d\n", pb, pw); if((B[pb].w>W[pw].w+x && pb<cntB && pw<cntW) || pb>=cntB) { int fa=find(W[pw].u), fb=find(W[pw].v); //printf("u=%d v=%d fu=%d fv=%d pu=%d pv=%d\n", W[pw].u, W[pw].v, fa, fb, p[W[pw].u], p[W[pw].v]); if(fa!=fb) { if(rank[fa]<rank[fb]) { p[fa]=fb; rank[fb]+=rank[fa]; } else p[fb]=fa, rank[fa]+=rank[fb]; ans+=W[pw].w; res++; } pw++; } else { int fa=find(B[pb].u), fb=find(B[pb].v); if(fa!=fb) { if(rank[fa]<rank[fb]) { p[fa]=fb; rank[fb]+=rank[fa]; } else p[fb]=fa, rank[fa]+=rank[fb]; ans+=B[pb].w; } pb++; } } return res; } bool cmp(Edge a, Edge b) { return a.w<b.w; } int main() { int n, m, k; int T=0; //freopen("tree.in", "r", stdin); //freopen("tree1.out", "w", stdout); while (~scanf("%d%d%d", &n, &m, &k)) { cntB=cntW=0; for (int i=0; i<m; ++i) { int a, b, c, x; scanf("%d%d%d%d", &a, &b, &c, &x); if(x) B[cntB].u=a, B[cntB].v=b, B[cntB++].w=c; else W[cntW].u=a, W[cntW].v=b, W[cntW++].w=c; } sort(W, W+cntW, cmp); sort(B, B+cntB, cmp); int l=-101, r=101, mid; while (l<=r) { mid=(l+r)>>1; ans=0; int ld=kruskal(mid, n); ans=0; int rd=kruskal(mid-1, n); ans+=(rd-k)*mid; if(ld<=k && k<=rd)break; if(k<ld)l=mid+1; if(k>rd)r=mid-1; //printf("%d %d %d %d %d\n", l, r, ld, rd, ans); } printf("Case %d: %d\n", ++T, ans); } return 0; }
void merge(int a, int b, int color, int &pp, int &res) { int fa=find(a), fb=find(b); //printf("u=%d v=%d fu=%d fv=%d pu=%d pv=%d\n", W[pw].u, W[pw].v, fa, fb, p[W[pw].u], p[W[pw].v]); if(fa!=fb) { if(rank[fa]<rank[fb]) { p[fa]=fb; rank[fb]+=rank[fa]; } else p[fb]=fa, rank[fa]+=rank[fb]; if(!color)ans+=W[pp].w; else ans+=B[pp].w; if(!color)res++; } pp++; } int kruskal(int x, int n)/// 同权值先加黑边 { init(n); int pb=0, pw=0; int res=0; while (pb<cntB && pw<cntW) { //printf("pb=%d pw=%d\n", pb, pw); if((B[pb].w>W[pw].w+x && pb<cntB && pw<cntW) || pb>=cntB) { merge(W[pw].u, W[pw].v, 0, pw, res); } else { merge(B[pw].u, B[pw].v, 1, pb, res); } } while (pb<cntB)merge(B[pw].u, B[pw].v, 1, pb, res); while (pw<cntW)merge(W[pw].u, W[pw].v, 0, pw, res); return res; }