陳題, 有篇論文, 利用了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;
}