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. (4)分布式下的爬虫Scrapy应该如何做-规则自动爬取及命令行下传参

    本次探讨的主题是规则爬取的实现及命令行下的自定义参数的传递,规则下的爬虫在我看来才是真正意义上的爬虫. 我们选从逻辑上来看,这种爬虫是如何工作的: 我们给定一个起点的url link ,进入页面之后提 ...

  2. 「日常训练」「小专题·图论」Domino Effect(1-5)

    题意 分析 这题几乎就是一条dijkstra的问题.但是,如何考虑倒在中间? 要意识到这题求什么:单源最短路的最大值.那么有没有更大的?倒在中间有可能会使它更大. 但是要注意一个问题:不要把不存在的边 ...

  3. 02-Mysql数据库----初识

    什么是数据(Data) 描述事物的符号记录称为数据,描述事物的符号既可以是数字,也可以是文字.图片,图像.声音.语言等,数据由多种表现形式,它们都可以经过数字化后存入计算机 在计算机中描述一个事物,就 ...

  4. 5.爬虫 requests库讲解 高级用法

    0.文件上传 import requests files = {'file': open('favicon.ico', 'rb')} response = requests.post("ht ...

  5. Python登录小程序

    ------------------------------------------------- 主要实现功能 1.用户输入用户名,在用户名文件中查找对应的用户,若无对应用户名则打印输入错误 2.用 ...

  6. 微信公众号开发java框架:wx4j(MaterialUtils篇)

    wx4j-MaterialUtils的使用 函数说明:上传永久视频素材 参数:文件路径.视频描述(通过setter填充内容即可) 返回值:微信服务器返回的json字符串 public static S ...

  7. C++STL中的vector的简单实用

    [原创] 使用C++STL中的vector, #include <stdio.h> #include<stdlib.h> #include<vector> usin ...

  8. wutianqi 博客 母函数

    母函数(Generating function)详解 — Tanky Woo 在数学中,某个序列的母函数(Generating function,又称生成函数)是一种形式幂级数,其每一项的系数可以提供 ...

  9. lintcode-133-最长单词

    133-最长单词 给一个词典,找出其中所有最长的单词. 样例 在词典 { "dog", "google", "facebook", &quo ...

  10. Bjarne Stroustrup 语录1

    1. 请谈谈C++书.  没有,也不可能有一本书对于所有人来说都是最好的.不过对于那些真正的程序员来说,如果他喜欢从“经典风格”的书中间学习一些新的概念和技术,我推荐我的The C++ Program ...