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. 数据库学习(四)with as (补充 nvl 和 count 函数)

    with as 的专业解释我这就不详细说明了,我这就梳理下我自己的实践应用,就是根据某个条件查询出结果集放在一个临时表里面,可以创建多个临时表,然后再从这些临时表中查询出要的数据. 参考资料:http ...

  2. fiddler抓包-简单易操作(二)

    Fiddler抓包简介 原理:fiddler是通过改写HTTP代理,客户端和服务器进行交互时,数据会从他那里通过,来监控和截取数据.我是这样理解的,如果不对,欢迎指正.如下图: 如果想要抓到数据包,首 ...

  3. git部署详解

    1.1 关于版本控制 1.1.1 本地版本控制 本地版本控制系统 许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别.这么做唯一的 好处就是简单,但是特别容易犯错.有 ...

  4. annoy安装

    yum install gcc-c++ #linux下需安装c++编译器 sudo pip install annoy

  5. 搭建高可用的Eureka注册中心

    搭建高可用的Eureka注册中心 一.搭建高可用的Eureka的作用 当服务器因种种原因导致Eureka注册中心(后面简称Eureka)服务当机(服务器跪了,异常关闭停止服务).这样就会影响到整个业务 ...

  6. HDU 1271 整数对(思路题)

    假设删除第k位,把整数A表示成如下形式: A = a * 10^(k+1) + b * 10 ^k + c; 则: B = a * 10^k + c; N = A + B = (11*a+b)*10^ ...

  7. HDU 4722 Good Numbers(位数DP)(2013 ACM/ICPC Asia Regional Online ―― Warmup2)

    Description If we sum up every digit of a number and the result can be exactly divided by 10, we say ...

  8. Week1 Team Homework #1 from Z.XML-对于学长项目《shield star》的思考和看法

    试用了一下学长黄杨等人开发的<shield star>游戏.                      其实作为一个学弟,我对cocos2d-x引擎还算是比较了解,所以对于这样一款很“典型 ...

  9. oracle视图就是封装了一条写好的sql语句 可通过视图修改表结构 ; oracle需要手动创建序列

    create sequence student_sid; --创建序列 oracle只能通过手动方式创建序列

  10. Python数据分析(三)pandas resample 重采样

    下方是pandas中resample方法的定义,帮助文档http://pandas.pydata.org/pandas-docs/stable/timeseries.html#resampling中有 ...