【MST】P2323 [HNOI2006]公路修建问题
Description
给定 \(n\) 个点 \(m - 1\) 条无向边,每条边有两种边权,贵一点的和便宜一点的。要求至少选择 \(k\) 条贵边使得图联通且花费最大的边权值最小。
Input
第一行是三个整数 \(n,m,k\)。
下面 \(m - 1\) 行每行描述一条边。
Output
输出最小花费与方案。
Hint
\(1~\leq~n~\leq~10000,1~\leq~m~\leq~20000\) ,边权 \(\leq~30000\)
Solution
两种做法。
首先题面已经非常明显的提示二分答案,于是我们二分最大花费,然后对两种边分别跑克鲁斯卡尔即可。由于较贵的边有限制,所以我们优先跑较贵的边的克鲁尔卡尔,跑完再跑便宜的边。
这样做的正确性是因为对于一条合法的两个不同的联通块之间的边,他被枚举到的时候是一定会被选择的。
注意到克鲁斯卡尔的复杂度为排序 \(O(m \log m)\),主算法 \(O(m~\alpha(n))\),本题不需要排序,但是一共跑了 \(O(\log c)\) 次主算法,所以总体时间复杂度为 \(O(m~\alpha(n)~\log c)\)
看起来那个 \(O(\alpha(n)~\times~\log c)\) 非常不优美,直觉告诉我这一部分是可以被去掉的,于是我们考虑优化上述算法。
注意到我们在上述算法中对边权是否排序是不影响答案的,所以我们不妨对贵的边进行排序。然后我们发现对贵的边跑 MST 的时候等价于选择出至少 \(k\) 条不同联通块之间的边。于是我们直接贪心的选出 \(k\) 条边权最小的且能构成合法联通块的边。即对贵的边跑 MST 直到选出 \(k\) 条边。
我们发现剩下的边选贵的还是便宜的是无所谓的,于是我们将他们混在一起排序,再对剩下的边跑一遍 MST 即可得答案。
这样做我们一共进行了 \(O(1)\) 次克鲁斯卡尔 \(O(m~\alpha(n))\) 的主算法,排序复杂度为 \(O(m~\log m)\)。由于 于是总复杂度 \(O(m~(\log m + \alpha(n))\)。成功的去掉了乘积
Code
\(O(m~\alpha(n)~\log c)\):
#include <cstdio>
#include <vector>
#include <algorithm>
#define ci const int
#define cl const long long
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 10010;
const int maxm = 20010;
struct Edge {
int from, to, v, id;
inline void build(int x, int y, int z, int w) {
from = x; to = y; v = z; id = w;
}
inline bool operator<(const Edge &_others) const {
return this->v < _others.v;
}
};
Edge com[maxm], expen[maxm];
int n, m, k, dn;
std::vector< std::pair<int, int> > MU;
int ufs[maxn], rk[maxn];
bool check(int, bool);
int find(ci);
void unionn(int, int);
int main() {
freopen("1.in", "r", stdin);
qr(n); qr(k); qr(m); dn = n - 1;
for (int i = 1, a, b, c; i < m; ++i) {
a = b = c = 0; qr(a); qr(b); qr(c);
expen[i].build(a, b, c, i);
c = 0; qr(c);
com[i].build(a, b, c, i);
}
int l = 1, r = 30000, mid, ans = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid, false)) ans = mid, r = mid - 1;
else l = mid + 1;
}
qw(ans, '\n', true); check(ans, true); std::sort(MU.begin(), MU.end());
for (int i = 0; i < dn; ++i) {
qw(MU[i].first, ' ', true); qw(MU[i].second, '\n', true);
}
return 0;
}
bool check(int x, bool rec) {
for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
int cnt = 0;
for (int i = 1; i < m; ++i) if (expen[i].v <= x) {
int fa = find(expen[i].from), fb = find(expen[i].to);
if (fa == fb) continue;
unionn(fa, fb);
if (rec) MU.push_back(std::make_pair(expen[i].id, 1));
++cnt;
}
if (cnt < k) return false;
for (int i = 1; i < m; ++i) if (com[i].v <= x) {
int fa = find(com[i].from), fb = find(com[i].to);
if (fa == fb) continue;
unionn(fa, fb);
if (rec) MU.push_back(std::make_pair(com[i].id, 2));
++cnt;
if (cnt == dn) return true;
}
return cnt == dn;
}
int find(int x) {return ufs[x] == x ? x : ufs[x] = find(ufs[x]);}
void unionn(int fa, int fb) {
if (rk[fa] < rk[fb]) ufs[fa] = fb;
else if (rk[fb] < rk[fa]) ufs[fb] = fa;
else ufs[fb] = fa, ++rk[fa];
}
\(O(m (\log m + \alpha(n)))\):
#include <cstdio>
#include <vector>
#include <algorithm>
#define ci const int
#define cl const long long
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 20010;
const int maxm = 40010;
struct Edge {
int from, to, v, id, tp;
inline void build(int x, int y, int z, int w, int u) {
from = x; to = y; v = z; id = w; tp = u;
}
inline bool operator<(const Edge &_others) const {
return this->v < _others.v;
}
};
Edge expen[maxn], tmp, edge[maxm];
std::vector< std::pair<int, int> >ans;
int n, k, m, ecnt, maxans;
int ufs[maxn], rk[maxn];
int find(int);
void unionn(int, int);
void printans();
int main() {
freopen("1.in", "r", stdin);
qr(n); qr(k); qr(m); int dn = n - 1;
for (int i = 1, a, b, c; i < m; ++i) {
a = b = c = 0; qr(a); qr(b); qr(c);
expen[i].build(a, b, c, i, 1); c = 0; qr(c);
edge[++ecnt].build(a, b, c, i, 2);
}
std::sort(expen + 1, expen + m);
int cnt = 0;
for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
for (int i = 1; i < m; ++i) {
int fa = find(expen[i].from), fb = find(expen[i].to);
if (fa == fb) {
edge[++ecnt] = expen[i];
} else {
unionn(fa, fb);
maxans = expen[i].v;
ans.push_back(std::make_pair(expen[i].id, 1));
if ((++cnt) == k) break;
}
}
std::sort(edge + 1, edge + 1 + ecnt);
for (int i = 1; cnt != dn; ++i) {
int fa = find(edge[i].from), fb = find(edge[i].to);
if (fa == fb) continue;
unionn(fa, fb);
maxans = std::max(maxans, edge[i].v);
ans.push_back(std::make_pair(edge[i].id, edge[i].tp));
++cnt;
}
printans();
return 0;
}
int find(int x) {return ufs[x] == x ? x : ufs[x] = find(ufs[x]);}
void unionn(int a, int b) {
if (rk[a] < rk[b]) ufs[a] = b;
else if (rk[a] > rk[b]) ufs[b] = a;
else ufs[b] = a, ++rk[b];
}
void printans() {
qw(maxans, '\n', true);
std::sort(ans.begin(), ans.end());
for (auto i : ans) {
qw(i.first, ' ', true); qw(i.second, '\n', true);
}
}
【MST】P2323 [HNOI2006]公路修建问题的更多相关文章
- 洛谷 P2323 [HNOI2006]公路修建问题 解题报告
P2323 [HNOI2006]公路修建问题 题目描述 输入输出格式 输入格式: 在实际评测时,将只会有m-1行公路 输出格式: 思路: 二分答案 然后把每条能加的大边都加上,然后加小边 但在洛谷的题 ...
- 1196/P2323: [HNOI2006]公路修建问题
1196: [HNOI2006]公路修建问题 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2191 Solved: 1258 Descriptio ...
- 洛谷P2323 [HNOI2006] 公路修建问题 [二分答案,生成树]
题目传送门 公路修建问题 题目描述 OI island是一个非常漂亮的岛屿,自开发以来,到这儿来旅游的人很多.然而,由于该岛屿刚刚开发不久,所以那里的交通情况还是很糟糕.所以,OIER Associa ...
- P2323 [HNOI2006]公路修建问题
题目描述 输入输出格式 输入格式: 在实际评测时,将只会有m-1行公路 输出格式: 输入输出样例 输入样例#1: 4 2 5 1 2 6 5 1 3 3 1 2 3 9 4 2 4 6 1 输出样例# ...
- 洛谷 P2323 [HNOI2006]公路修建问题
题目描述 输入输出格式 输入格式: 在实际评测时,将只会有m-1行公路 输出格式: 输入输出样例 输入样例#1: 4 2 5 1 2 6 5 1 3 3 1 2 3 9 4 2 4 6 1 3 4 4 ...
- BZOJ 1196: [HNOI2006]公路修建问题( MST )
水题... 容易发现花费最大最小即是求 MST 将每条边拆成一级 , 二级两条 , 然后跑 MST . 跑 MST 时 , 要先加 k 条一级road , 保证满足题意 , 然后再跑普通的 MST . ...
- 【最小生成树】BZOJ 1196: [HNOI2006]公路修建问题
1196: [HNOI2006]公路修建问题 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1435 Solved: 810[Submit][Sta ...
- bzoj 1196: [HNOI2006]公路修建问题 二分+并查集
题目链接 1196: [HNOI2006]公路修建问题 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1576 Solved: 909[Submit ...
- BZOJ_1196_[HNOI2006]公路修建问题_kruskal+二分答案
BZOJ_1196_[HNOI2006]公路修建问题_kruskal+二分答案 题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1196 分析: ...
随机推荐
- DevOps on AWS之OpsWorks初体验
AWS OpsWorks 是一款配置管理服务,提供 Chef 和 Puppet 的托管EC2虚拟机实例.Chef 和 Puppet 是自动化平台,允许用户使用代码来自动配置服务器.用户借助OpsWor ...
- NodeJS http模块
Node.js提供了http模块,用于搭建HTTP服务端和客户端. 创建Web服务器 /** * node-http 服务端 */ let http = require('http'); let ur ...
- LeetCode 303. Range Sum Query - Immutable (C++)
题目: Given an integer array nums, find the sum of the elements between indices iand j (i ≤ j), inclus ...
- 作业要求 20181127-5 Beta发布用户使用报告
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2450 一.用户反馈 反馈截图(部分) 三.用户反馈情况统计图
- Java将json字符串转成map
Map<String, Object> map = (Map<String, Object>) JSONUtils.parse(result)
- vim相关命令单独记载
1. 无敌的可扩展性 1.1 可扩展性给了软件强大的生命 曾几何时,Windows用户对软件的可扩展性没有概念,他们只能对他们使用的软件进行非常有限的定制.扩展软件的权利保留在软件开发者手中.软件的使 ...
- connect by prior id= pid start with id='1' 树结构查询
基础表创建: with temp as ( ' id, '' pid from dual union all ' pid from dual union all ' pid from dual uni ...
- iOS开发学习-放大长图与屏幕等宽
/* 需要得到一个图片的放大比例,这个比例就是屏幕的宽度与图片真实宽度的比值 */ CGFloat newZoomScale = LZ_SCREEN_WIDTH / [_photoImageView. ...
- 关于 Java连接sql的转载
Java连接SQL Server 2000数据库时,有两种方法: (1)通过Microsoft的JDBC驱动连接.此JDBC驱动共有三个文件,分别是mssqlserver.jar.msutil.jar ...
- whu 1538 - B - Stones II 01背包
题目链接: http://acm.whu.edu.cn/land/problem/detail?problem_id=1538 Problem 1538 - B - Stones II Time Li ...