Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行
每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
Sample Output
2
HINT
数据规模和约定
0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。
题解
二分,随着白色边权的值增加,白色边出现在生成树里面的个数减少,有单调性,所以可以二分白色边权的增加量,计算白色边出现次数。
代码
#include<bits/stdc++.h>
#define N 500005
#define ll long long
#define inf 100000000
#define mod 100000000
using namespace std;
inline 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;
}
int f[1000001],n,m,need,tmp,ans,cnt,ans2;
struct node{int st,ed,c,v;}a[100010];
int find(int i)
{
if (f[i]!=i) f[i]=find(f[i]);
return f[i];
}
bool cmp(node a,node b){if (a.v==b.v) return a.c<b.c;else return a.v<b.v;}
void solve()
{
sort(a+1,a+m+1,cmp);
for (int i=1;cnt!=n-1;i++)
{
int r1=find(a[i].st),r2=find(a[i].ed);
if (r1!=r2){f[r1]=r2;cnt++;if (a[i].c==0) tmp++;ans+=a[i].v;}
}
}
int main()
{
n=read();m=read();need=read();
for (int i=1;i<=m;i++)
{
a[i].st=read();a[i].ed=read();a[i].v=read();a[i].c=read();
a[i].st++;a[i].ed++;
}
int l=-105,r=105;
while (l<=r)
{
int mid=(l+r)>>1;
for (int i=1;i<=m;i++) if (a[i].c==0) a[i].v+=mid;
for (int i=1;i<=n;i++) f[i]=i;
ans=tmp=cnt=0;
solve();
if (tmp>=need){l=mid+1;ans2=ans-need*mid;}else r=mid-1;
for (int i=1;i<=m;i++) if (a[i].c==0) a[i].v-=mid;
}
printf("%d",ans2);
return 0;
}