题目描述
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 needneed 条白色边的生成树。
题目保证有解。
输入格式
第一行 V,E,needV,E,need 分别表示点数,边数和需要的白色边数。
接下来 EE 行,每行 s,t,c,cols,t,c,col 表示这边的端点(点从 00 开始标号),边权,颜色(00 白色 11 黑色)。
输出格式
一行,表示所求生成树的边权和。
输入输出样例
输入 #1复制
2 2 1 0 1 1 1 0 1 2 0
输出 #1复制
2
说明/提示
对于 5\%5% 的数据,V\leq 10V≤10。
对于另 15\%15% 的数据,V\leq 15V≤15。
对于 100\%100% 的数据,V\leq 5\times10^4,E\leq 10^5V≤5×104,E≤105。
所有数据边权为 [1,100][1,100] 中的正整数。
By WJMZBMR
有关思路建议参考第二个洛谷博客,下面只加一点注释
#include<iostream>
#include<algorithm>
#include<map>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<numeric>
#include<stack>
#include<queue>
#include<set>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
//怎么说呢,WQS二分应用前提是能形成凸包,但是证明肯定是很难的。
//不如就感性理解一下,如肯定有不管选几个的最优情况,如果加限制,那么越偏离这个最优个数的话
//只会越糟糕(由于接近有很多种糟糕情况,但是接近最优的那些还能勉强挑一些影响小的,但是越偏
//离只会选到的是越糟糕的影响,斜率就单调变化了,而且总的是能形成凸包的)
//或者如加权值去参与总的选取不会影响被加者的“优先级”,只会影响它们和另一类之间的“优先级”,
//毕竟问题中涉及的也都是加减问题,似乎本质上就是线性的。(试图说服自己
const int maxn=1e5+5;
struct edge{
int u,v,w,c;//0是白色
bool operator<(const edge &a)const{
return w<a.w||w==a.w&&c<a.c;
}
}g[maxn];
int fa[maxn],rk[maxn];
void make(){iota(fa,fa+maxn,0);for(int i=0;i<maxn;i++)rk[i]=1;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool same(int a,int b){a=find(a);b=find(b);return a==b;}
void unite(int a,int b){if(same(a,b))return;a=find(a);b=find(b);if(rk[a]<rk[b]){fa[a]=b;rk[b]+=rk[a];}else{fa[b]=a;rk[a]+=rk[b];}}
int n,m,k,cnt,sum,ans,wcnt=0;//k即need
void kruskal()
{
make();
sum=0;
cnt=0;
sort(g,g+m);
for(int i=0;i<m;i++)
{
if(same(g[i].u,g[i].v))continue;
unite(g[i].u,g[i].v);
sum+=g[i].w;
if(g[i].c==0)cnt++;
}
}
int main()
{
ios_base::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
cin>>g[i].u>>g[i].v>>g[i].w>>g[i].c;
if(g[i].c==0)wcnt++;
}
int l=-101,r=101;
while(l+1<r)
{
int mid=(r+l)/2;
for(int i=0;i<m;i++)if(g[i].c==0)g[i].w+=mid;
kruskal();
for(int i=0;i<m;i++)if(g[i].c==0)g[i].w-=mid;
if(cnt>=k)//这个等于号放这边还是要结合上面的运算符重载来的,具体不说了,从例子意会即可,如例子555444
//这六条边,前三条为黑色,后三条为白色,要求一条白色,并假设其中三条白色边、黑边均可以分别组成生成树,
//那么按照排序来,如果+1则得到三条白边,+2则得到3条黑边,那么由于一定有答案,则可以取+1的并且把两条
//白边变成黑边;而若是直接按照权值排序则是+0得到三条白边,+1得到三条黑边,那么再按照这样减则不符合道
//理了,就这么意会即可!
{
l=mid;
ans=sum-mid*k;//想想上面第一种排序方法以及怎么个减去法即可理解
}
else r=mid;
}
cout<<ans;
return 0;
}