[洛谷 P5633] 最小度限制生成树(WQS 二分) | 错题本

文章目录

题目

[洛谷 P5633] 最小度限制生成树

分析

s s s 的邻接边弄一个 Δ \Delta Δ 值加在边权上,然后做最小生成树,易知 Δ \Delta Δ 越大, s s s 的度越小,因此可以 WQS 二分。实现把 s s s 的邻接边和非邻接边分别排好序,做的时候归并即可。

代码

#include <bits/stdc++.h>

int Read() {
	int x = 0; bool f = false; char c = getchar();
	while (c < '0' || c > '9')
		f |= c == '-', c = getchar();
	while (c >= '0' && c <= '9')
		x = x * 10 + (c ^ 48), c = getchar();
	return f ? -x : x;
}

typedef std::pair<int, int> PII;

const int MAXN = 50000;
const int MAXM = 500000;
const int INF = 0x3f3f3f3f;

int N, M, S, K;

struct Edge {
	int u, v, w;
} E1[MAXM + 5], E2[MAXM + 5], E[MAXM + 5];
int Cnt1, Cnt2, Cnt;

inline bool Comp(const Edge &i, const Edge &j) {
	return i.w < j.w;
}

void MergeSort(const int &dlt) {
	Cnt = 0;
	int i = 1, j = 1;
	while (i <= Cnt1 && j <= Cnt2) {
		if (E1[i].w + dlt < E2[j].w)
			E[++Cnt] = E1[i++], E[Cnt].w += dlt;
		else E[++Cnt] = E2[j++];
	}
	while (i <= Cnt1)
		E[++Cnt] = E1[i++], E[Cnt].w += dlt;
	while (j <= Cnt2)
		E[++Cnt] = E2[j++];
}

int Par[MAXN + 5];

int Find(const int &u) {
	return Par[u] == u ? u : (Par[u] = Find(Par[u]));
}

PII Kruscal(const int &dlt) {
	MergeSort(dlt);
	for (int i = 1; i <= N; i++)
		Par[i] = i;
	PII ret(0, 0);
//	puts("");
	for (int i = 1; i <= Cnt; i++) {
		int u = E[i].u, v = E[i].v;
//		printf("%d %d\n", u, v);
		if (Find(u) != Find(v)) {
			Par[Find(u)] = Find(v);
			ret.first += E[i].w;
			ret.second += u == S || v == S;
		}
	}
	return ret;
}

int main() {
	N = Read(), M = Read(), S = Read(), K = Read();
	for (int i = 1; i <= M; i++) {
		int u = Read(), v = Read(), w = Read();
		if (u == S || v == S)
			E1[++Cnt1] = { u, v, w };
		else E2[++Cnt2] = { u, v, w };
	}
	std::sort(E1 + 1, E1 + Cnt1 + 1, Comp);
	std::sort(E2 + 1, E2 + Cnt2 + 1, Comp);
	int lft = -INF, rgt = INF;
	while (lft + 1 < rgt) {
		int mid = (lft + rgt) >> 1;
		PII cur = Kruscal(mid);
		if (cur.second < K)
			rgt = mid;
		else lft = mid;
	}
	PII cur = Kruscal(lft);
	if (cur.second != K)
		puts("Impossible");
	else printf("%d\n", cur.first - lft * K);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值