陈题, 有篇论文, 利用了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;
}