Brief description

给定一个无向图,求从1到n的一条路径使得这条路径上最大的a和b最小。

Algorithm Design

以下内容选自某HN神犇的blog

双瓶颈的最小生成树的感觉,可以首先按a值排序,然后一条边一条边的加入.

  如果之前连接的两点还未连通,那么连上先满足最后连通性的必要.

  如果之前连接的两点已经连通,那么就在原来的路径上找到一条b值最大的,然后删掉原来的,加上现在的边,保证最优性的需要.

  这样会导致最大的b值的减小,但是如果之前1,n已经连通,也会造成最大的a值的增大

  [因为是按a排序,在连通前的操作都是不管a值的,只以最后一次加的边为最大[所以之前的替换操作只会让这个路径更优],但是连通后,添加的边就会让a值增大[不一定会更优]],这就需要在多种方案间选出最优.

  上面说得很轻巧,现在我们想想怎么完成上述操作.

  总共要对每条边处理一次,每次需要连边或者在两点之前的链上找最大值.找到之后有删边的操作.

  支持这么多操作的数据结构有什么?[注意我们连接的一定是一棵树[或是一片森林]...不然就浪费了...]

  lca似乎不兹瓷啊,因为是动态的,哦,那就是动态树了.

  动态树中带边权的怎么处理呢?可以将所有实点的值定为0,连(u,v)边改为连(u,x)和(x,v),x的值代表这条边的边权.

  [p.s]有的同学会觉得我连(u,v)把值记在u上或者v上就可以了...每次splay的时候,只有根节点保留的是在原树中连接上个部分的边权,其它的在splay的时候交换.[<-这一步是可以实现的]

    有的同学觉得我这样不就可以了么?然而...你还有个东西叫Access(),你每次会将原来本来是链的顶部才能连的边,给了当前splay的根,然后连通之后再splay,鬼才找的到原来的边是什么?..当然上面的"有的同学"都是说的笔者..有的大神说不定还是可以不加虚拟边点过的...

Code

#include <algorithm>
#include <cstdio>
int n, m;
const int maxn = 50010;
const int maxm = 100010;
const int maxv = maxn + maxm;
struct edge {
int u, v, a, b;
} e[maxm << 1];
bool cmp1(edge a, edge b) { return a.a < b.a; }
int f[maxn], val[maxv], fa[maxv], ch[maxv][2], rev[maxv], max[maxv];
int findf(int x) { return f[x] == x ? x : f[x] = findf(f[x]); }
void pushdown(int x) {
if (rev[x]) {
rev[x] ^= 1;
rev[ch[x][0]] ^= 1;
rev[ch[x][1]] ^= 1;
std::swap(ch[x][0], ch[x][1]);
}
}
void update(int x) {
max[x] = x;
if (val[max[ch[x][0]]] > val[max[x]])
max[x] = max[ch[x][0]];
if (val[max[ch[x][1]]] > val[max[x]])
max[x] = max[ch[x][1]];
}
bool isroot(int x) { return (ch[fa[x]][0] != x) && (ch[fa[x]][1] != x); }
void zig(int x) {
int y = fa[x], z = fa[y], l = (ch[y][1] == x), r = l ^ 1;
if (!isroot(y))
ch[z][ch[z][1] == y] = x;
fa[ch[y][l] = ch[x][r]] = y;
fa[ch[x][r] = y] = x;
fa[x] = z;
update(y);
update(x);
}
void splay(int x) {
int s[maxv], top = 0;
s[++top] = x;
for (int i = x; !isroot(i); i = fa[i])
s[++top] = fa[i];
while (top)
pushdown(s[top--]);
for (int y; !isroot(x); zig(x))
if (!isroot(y = fa[x]))
zig((ch[y][0] == x) == (ch[fa[y]][0] == y) ? y : x);
update(x);
}
void access(int x) {
for (int t = 0; x; t = x, x = fa[x]) {
splay(x);
ch[x][1] = t;
update(x);
}
}
void makeroot(int x) {
access(x);
splay(x);
rev[x] ^= 1;
}
void link(int x, int y) {
makeroot(x);
fa[x] = y;
}
void cut(int x, int y) {
makeroot(x);
access(y);
splay(y);
ch[y][0] = fa[x] = 0;
update(y);
}
void split(int x, int y) {
makeroot(y);
access(x);
splay(x);
}
int ans = 0x3f3f3f;
int main() {
freopen("input", "r", stdin);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++) {
scanf("%d %d %d %d", &e[i].u, &e[i].v, &e[i].a, &e[i].b);
}
std::sort(e + 1, e + 1 + m, cmp1);
for (int i = 1; i <= m; i++) {
int u = e[i].u, v = e[i].v, x = findf(u), y = findf(v);
if (x == y) {
split(u, v);
int t = max[u];
if (val[t] > e[i].b) {
cut(e[t - n].u, t);
cut(e[t - n].v, t);
} else {
if (findf(1) == findf(n)) {
split(1, n);
int t = max[1];
ans = std::min(ans, e[i].a + val[t]);
}
continue;
}
}
if (x != y) {
f[x] = y;
}
val[n + i] = e[i].b;
max[n + i] = n + i;
link(u, i + n);
link(v, i + n);
if (findf(1) == findf(n)) {
split(1, n);
int t = max[1];
ans = std::min(ans, e[i].a + val[t]);
}
}
printf("%d\n", ans == 0x3f3f3f ? -1 : ans);
}

[bzoj3669][Noi2014]魔法森林——lct的更多相关文章

  1. bzoj3669: [Noi2014]魔法森林 lct版

    先上题目 bzoj3669: [Noi2014]魔法森林 这道题首先每一条边都有一个a,b 我们按a从小到大排序 每次将一条路劲入队 当然这道题权在边上 所以我们将边化为点去连接他的两个端点 当然某两 ...

  2. bzoj3669: [Noi2014]魔法森林 lct

    记得去年模拟赛的时候好像YY出二分答案枚举a,b的暴力,过了55欸 然后看正解,为了将两维变成一维,将a排序,模拟Kruskal的加边过程,同时维护1到n的最大值,加入一条边e(u,v,a,b)时有以 ...

  3. BZOJ 3669: [Noi2014]魔法森林( LCT )

    排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...

  4. bzoj 3669: [Noi2014]魔法森林 (LCT)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec  ...

  5. [NOI2014]魔法森林 LCT

    题面 [NOI2014]魔法森林 题解 一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考 ...

  6. loj2245 [NOI2014]魔法森林 LCT

    [NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...

  7. 【BZOJ3669】[Noi2014]魔法森林 LCT

    终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...

  8. BZOJ3669[Noi2014]魔法森林——kruskal+LCT

    题目描述 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住 ...

  9. BZOJ3669: [Noi2014]魔法森林(瓶颈生成树 LCT)

    Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 3558  Solved: 2283[Submit][Status][Discuss] Descript ...

随机推荐

  1. 思杰VDI提示“The VDI is not available”

    前言:困扰已久的问题终于解决. 问题:客户反馈无法连接VDI. 解决过程:1.登录后台查看VDI状态为关机状态尝试重新启动提示如下图: 2.判断此VDI的启动盘出现问题(注:本人环境无数据盘) 3.查 ...

  2. Oracle physical dataguard with broker部署

    一.环境说明 主库:10.110.96.88 备库:10.110.96.87 数据库实例:gisc 二.主库操作 1.开启force logging ALTER DATABASE FORCE LOGG ...

  3. 【志银】NYOJ《题目490》翻译

    1.题目:翻译 1.1.题目链接 http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=490 1.2.题目内容 2.解题分析 题目输入输出格式描述不 ...

  4. 如何使用remix

    ---恢复内容开始--- 教程 | [Ethereum 智能合约开发笔记]使用 Remix Ajian | 3. Feb, 2018 | 621 次阅读 教程 Remix 开发工具 之前有自己开发过E ...

  5. Window.open()方法参数详解总结(转)

    1, 最基本的弹出窗口代码   window.open('page.html'); 2, 经过设置后的弹出窗口   window.open('page.html', 'newwindow', 'hei ...

  6. django之上传文件和图片

    文件上传:文件上传功能是网站开发中必定会使用到的技术,在django项目中也是如此,下面会详细讲述django中上传文件的前端和后端的具体处理步骤: 前端HTML代码实现: 1.在前端中,我们需要填入 ...

  7. 搭建Elasticsearch 5.4分布式集群

    多机集群中的节点可以分为master nodes和data nodes,在配置文件中使用Zen发现(Zen discovery)机制来管理不同节点.Zen发现是ES自带的默认发现机制,使用多播发现其它 ...

  8. [STL] 遍历删除两个vector中交集

    #include <vector> #include <string> #include <algorithm> using namespace std; int ...

  9. 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  10. [CF912B]New Year's Eve

    题意:在1~n中选不超过m个数,求其异或最大值 题解:经过找规律发现如果m为1,输出n,不然输出最小的不超过n的2^k-1 C++ Code: #include<cstdio> using ...