题目大意:给定 N 个点,M 条边的无向图,支持两种操作:动态删边和查询任意两点之间路径上边权的最大值最小是多少。

题解:

引理:对原图求最小生成树,可以保证任意两点之间的路径上边权的最大值取得最小值。

证明:任取两点 x, y,若 x, y 的路径上最大值最小的边不在最小生成树的路径上,可以将那条边加入最小生成树中,并删去由这条边的加入所带来的环中边权最大的那条边,可以使得最小生成树更小,产生矛盾,证毕。

有了引理之后,问题转化成了维护支持动态删边的最小生成树。发现删边可能会导致最少生成树不断发生变化,每次变化都需要重构生成树,时间复杂度较高。可以采用离线询问,转化成倒序加边的最小生成树的维护,每次加一条边时,只需删除环上最大的那条边即可。支持动态加边和删边,可以采用 lct 进行维护。最后,可以进行点边转化,即:将边缩为一个点,边权为点权,点的点权为 0 即可。

代码如下

#include <bits/stdc++.h>

using namespace std;

struct edge {
int x, y, z;
bool has;
bool operator<(const edge &rhs) {
return this->z < rhs.z;
}
}; struct UFS {
vector<int> f;
UFS(int n) {
f.resize(n + 1);
for (int i = 1; i <= n; i++) {
f[i] = i;
}
}
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x != y) {
f[x] = y;
return 1;
}
return 0;
}
}; struct node {
node *l, *r, *p;
int val, maxv, rev, id;
node (int _val = 0, int _id = 0) {
l = r = p = NULL;
val = maxv = _val;
id = _id;
rev = 0;
}
void unsafe_reverse() {
swap(l, r);
rev ^= 1;
}
void pull() {
maxv = val;
if (l != NULL) {
l->p = this;
maxv = max(maxv, l->maxv);
}
if (r != NULL) {
r->p = this;
maxv = max(maxv, r->maxv);
}
}
void push() {
if (rev) {
if (l != NULL) {
l->unsafe_reverse();
}
if (r != NULL) {
r->unsafe_reverse();
}
rev = 0;
}
}
};
bool is_root(node *v) {
if (v == NULL) {
return false;
}
return (v->p == NULL) || (v->p->l != v && v->p->r != v);
}
void rotate(node *v) {
node *u = v->p;
assert(u != NULL);
v->p = u->p;
if (v->p != NULL) { // work with father
if (u == v->p->l) {
v->p->l = v;
}
if (u == v->p->r) {
v->p->r = v;
}
}
if (v == u->l) {
u->l = v->r;
v->r = u;
}
if (v == u->r) {
u->r = v->l;
v->l = u;
}
u->pull();
v->pull();
}
void deal_with_push(node *v) {
static stack<node*> stk;
while (true) {
stk.push(v);
if (is_root(v)) {
break;
}
v = v->p;
}
while (!stk.empty()) {
stk.top()->push();
stk.pop();
}
}
void splay(node *v) {
deal_with_push(v);
while (!is_root(v)) {
node *u = v->p;
if (!is_root(u)) {
if ((u->p->l == u) ^ (u->l == v)) {
rotate(v);
} else {
rotate(u);
}
}
rotate(v);
}
}
void access(node *v) {
node *u = NULL;
while (v != NULL) {
splay(v);
v->r = u;
v->pull();
u = v;
v = v->p;
}
}
void make_root(node *v) {
access(v);
splay(v);
v->unsafe_reverse();
}
node *find_root(node *v) {
access(v);
splay(v);
while (v->l != NULL) {
v->push();
v = v->l;
}
splay(v);
return v;
}
void split(node *u, node *v) {
make_root(u);
access(v);
splay(v);
}
void link(node *u, node *v) {
if (find_root(u) == find_root(v)) {
return;
}
make_root(v);
v->p = u;
}
void cut(node *u, node *v) {
make_root(u);
if (find_root(v) == u && v->p == u && v->l == NULL) {
v->p = u->r = NULL;
u->pull();
}
}
node* find(node *v, int val) {
while (true) {
if (v->val == val) {
break;
}
if (v->l != NULL && v->l->maxv == val) {
v = v->l;
} else {
v = v->r;
}
}
return v;
} int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, m, q;
cin >> n >> m >> q;
vector<node*> t(n + m + 1);
for (int i = 1; i <= n; i++) {
t[i] = new node(0, i);
}
vector<edge> e(m + 1);
map<pair<int, int>, int> id;
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
if (x > y) {
swap(x, y);
}
e[i] = {x, y, z, 1};
id[{x, y}] = i;
}
vector<pair<int, pair<int, int>>> events(q + 1);
for (int i = 1; i <= q; i++) {
int opt, x, y;
cin >> opt >> x >> y;
if (x > y) {
swap(x, y);
}
events[i] = {opt, {x, y}};
if (opt == 2) {
e[id[{x, y}]].has = 0;
}
}
auto kruskal = [&](vector<edge> ee) {
sort(ee.begin() + 1, ee.end());
UFS ufs(n);
for (int i = 1; i <= m; i++) {
if (ee[i].has == 1) {
int x = ee[i].x, y = ee[i].y, z = ee[i].z;
if (ufs.merge(x, y) == 1) {
int _id = id[{x, y}] + n; // edge_id
t[_id] = new node(z, _id);
link(t[x], t[_id]);
link(t[y], t[_id]);
}
}
}
};
kruskal(e);
vector<int> ans;
for (int i = q; i >= 1; i--) {
int x = events[i].second.first;
int y = events[i].second.second;
int eid = id[{x, y}];
int z = e[eid].z;
if (events[i].first == 1) {
split(t[x], t[y]);
ans.push_back(t[y]->maxv);
} else {
split(t[x], t[y]);
if (t[y]->maxv > z) {
int _id = find(t[y], t[y]->maxv)->id; // edge id
int a = e[_id - n].x, b = e[_id - n].y;
cut(t[a], t[_id]);
cut(t[b], t[_id]);
t[eid + n] = new node(z, eid + n);
link(t[x], t[eid + n]);
link(t[y], t[eid + n]);
}
}
}
reverse(ans.begin(), ans.end());
for (auto v : ans) {
cout << v << endl;
}
return 0;
}

【洛谷P4172】水管局长的更多相关文章

  1. 洛谷P4172 [WC2006]水管局长 (LCT,最小生成树)

    洛谷题目传送门 思路分析 在一个图中,要求路径上最大边边权最小,就不难想到最小生成树.而题目中有删边的操作,那肯定是要动态维护啦.直接上LCT维护边权最小值(可以参考一下蒟蒻的Blog) 这时候令人头 ...

  2. 洛谷P4172 [WC2006]水管局长(lct求动态最小生成树)

    SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径, ...

  3. [洛谷P4172] WC2006 水管局长

    问题描述 SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水 ...

  4. 【Luogu】P4172水管局长(LCT)

    题目链接 有个结论是x到y的路径上最长边权值等于最小生成树上最长边权值,于是问题转化为最小生成树. 再考虑把问题反过来,删边变成加边. 于是变成动态维护最小生成树,LCT可以做到. #include& ...

  5. 【洛谷4172】 [WC2006]水管局长(LCT)

    传送门 洛谷 BZOJ Solution 如果不需要动态的话,那就是一个裸的最小生成树上的最大边权对吧. 现在动态了的话,把这个过程反着来,就是加边对吧. 现在问题变成了怎么动态维护加边的最小生成树, ...

  6. 洛谷.4172.[WC2006]水管局长(LCT Kruskal)

    题目链接 洛谷(COGS上也有) 不想去做加强版了..(其实处理一下矩阵就好了) 题意: 有一张图,求一条x->y的路径,使得路径上最长边尽量短并输出它的长度.会有<=5000次删边. 这 ...

  7. P4172 [WC2006]水管局长(LCT)

    P4172 [WC2006]水管局长 LCT维护最小生成树,边权化点权.类似 P2387 [NOI2014]魔法森林(LCT) 离线存储询问,倒序处理,删边改加边. #include<iostr ...

  8. P4172 [WC2006]水管局长

    P4172 [WC2006]水管局长 前言 luogu数据太小 去bzoj,他的数据大一些 思路 正着删不好维护 那就倒着加,没了 LCT维护他的最小生成树MST 树上加一条边肯定会有一个环 看看环上 ...

  9. P4172 [WC2006]水管局长 LCT维护最小生成树

    \(\color{#0066ff}{ 题目描述 }\) SC 省 MY 市有着庞大的地下水管网络,嘟嘟是 MY 市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的 ...

随机推荐

  1. 《ThinkPHP 5.0快速入门》 请求和响应

    1.请求对象 //传统调用$request = Request::instance();//实例化对象 $request->url();//获取当前的域名 //继承think\Controlle ...

  2. Tei-Wei Kuo

    一. A Commitment-based Management Strategy for the Performance and Reliability Enhancement of Flash-m ...

  3. 算法flink

    和Yarn-Cluster模式不同,Session模式的资源在启动Yarn-Session时候就已经启动了,后续提交的作业全都在已申请的资源空间内运行,比较适合小而多的作业 # 启动yarn-sess ...

  4. [目标检测] 从 R-CNN 到 Faster R-CNN

    R-CNN 创新点 经典的目标检测算法使用滑动窗法依次判断所有可能的区域,提取人工设定的特征(HOG,SIFT).本文则预先提取一系列较可能是物体的候选区域,之后仅在这些候选区域上用深度网络提取特征, ...

  5. [转帖]容器云之K8s自动化安装方式的选择

    容器云之K8s自动化安装方式的选择 时间 2016-12-05 19:10:53  极客头条 原文  http://geek.csdn.net/news/detail/127426 主题 Kubern ...

  6. DOM、BOM

    DOM DOM 是 W3C(万维网联盟)的标准. DOM 定义了访问 HTML 和 XML 文档的标准: “W3C 文档对象模型 (DOM) 是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新 ...

  7. 去除MFC特性之一

    先对文件读取路径进行去除,然后对程序中出现的其他地方进行慢慢去除. #include "WavIo.h" #include "mfcc.h" #include ...

  8. 屹今为止最好用的HTTP客户端命令行工具-接口调试神器HTTPie

    一.思考❓❔ 1.你用过哪些http客户端调试工具? Postman 不够灵活 需要打开客户端, 麻烦 学习成本高 Jmeter 臃肿 麻烦 学习成本高 curl 参数多, 记不住 不够灵活 主要在L ...

  9. ajax-springMVC提交表单的方式

    1.request参数提交(Form提交),适用于GET/POST request参数传递都会转换成 id=123&fileName=test.name&type=culture_ar ...

  10. Hive SQL查询效率提升之Analyze方案的实施

    0.简介 Analyze,分析表(也称为计算统计信息)是一种内置的Hive操作,可以执行该操作来收集表上的元数据信息.这可以极大的改善表上的查询时间,因为它收集构成表中数据的行计数,文件计数和文件大小 ...