洛谷P2619 [国家集训队]Tree I

题目描述

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值