Problem

给定一个\(n\)个结点,\(m\)条边的的无向图,每条边有两个权值\(ai,bi\).

现在从\(1\)出发,要到达\(n\),每次只能沿着\(ai\le A\)且\(bi\le B\)的边走,求\(min(A+B)\).

\(n\le 5*10^4,m\le 2*10^5\)

Solution

经典的LCT题,拆边权为点权就没了.

顺便说一下LCT这个神奇的数据结构。因为姿势有限,暂且不讨论它的势能分析。

定义

LCT = splay + 虚实剖分

每个节点有 至多一条 实边连向儿子, 实边 相连,构成了一条 实链.

对于每条实链用一个 splay 去维护,关键字就是在原树当中以 根节点 为基准的 深度

Access

最重要的操作.

Access(N)定义为改变从\(N\)到根节点这一段路的每个节点的实边.

在代码中用这个过程实现:

void Access(int x) {
for (int y = 0; x ; y = x, x = fa[x])
splay(x), tr[x][1] = y, update(x);
}
Makeroot

定义为把某个点设为根,用这个过程去实现:

void Makeroot(int x) {
Access(x);
splay(x);
reverse(x);
}
Link,cut

就显得很简单了

void link(int x,int y) {
Makeroot(x);
fa[x] = y;
}
void cut(int x, int y) {
Makeroot(x);
Access(y);
splay(y);
fa[tr[y][0]] = 0, tr[y][0] = 0;
update(y);
}
注意

rotate的打法必须要记住这样打:

void rotate(int x) {
int y = fa[x], t = son(x);
if (!Rt(y)) tr[fa[y]][son(y)] = x; // 这里一定要注意!!!
if (tr[x][1 - t]) fa[tr[x][1 - t]] = y;
fa[x] = fa[y], fa[y] = x, tr[y][t] = tr[x][1 - t], tr[x][1 - t] = y;
update(y), update(x);
}
Code
#include <bits/stdc++.h>

#define F(i, a, b) for (int i = a; i <= b; i ++)
#define ls tr[x][0]
#define rs tr[x][1] const int N = 2e5 + 10; using namespace std; int n, m, x, y, Ans, RT;
int v[N], tr[N][2], fa[N], rev[N], d[N], pt[N], fat[N];
struct edge {
int x, y, a, b;
friend bool operator < (edge a, edge b) { return a.a < b.a; }
} e[N]; int son(int x) { return tr[fa[x]][1] == x; }
bool Rt(int x) { return tr[fa[x]][son(x)] ^ x; } void reverse(int x) { swap(ls, rs), rev[x] ^= 1; }
void push(int x) {
if (!rev[x]) return;
reverse(ls), reverse(rs), rev[x] = 0;
} void update(int x) {
pt[x] = x;
if (v[pt[ls]] > v[pt[x]]) pt[x] = pt[ls];
if (v[pt[rs]] > v[pt[x]]) pt[x] = pt[rs];
} void rotate(int x) {
int y = fa[x], t = son(x);
if (!Rt(y)) tr[fa[y]][son(y)] = x;
if (tr[x][1 - t]) fa[tr[x][1 - t]] = y;
fa[x] = fa[y], fa[y] = x, tr[y][t] = tr[x][1 - t], tr[x][1 - t] = y;
update(y), update(x);
} void clear(int x) {
d[++ d[0]] = x;
while (!Rt(x)) d[++ d[0]] = x = fa[x];
while (d[0]) push(d[d[0] --]);
} void splay(int x) {
clear(x);
for (; !Rt(x); rotate(x))
if (!Rt(fa[x])) rotate(son(x) == son(fa[x]) ? fa[x] : x);
} void Access(int x) {
for (int y = 0; x ; y = x, x = fa[x])
splay(x), rs = y, update(x);
} void Makeroot(int x) { Access(x), splay(x), reverse(x); }
void link(int x, int y) { Makeroot(x), fa[x] = y; }
void cut(int x, int y) { Makeroot(x); Access(y); splay(y); fa[tr[y][0]] = 0, tr[y][0] = 0; update(y); } int Solve(int x, int y) {
Makeroot(x); Access(y), splay(y);
return pt[y];
}
int get(int x) { return !fat[x] ? x : fat[x] = get(fat[x]); } int main() {
scanf("%d%d", &n, &m);
F(i, 1, m) {
scanf("%d%d%d%d", &e[i].x, &e[i].y, &e[i].a, &e[i].b);
if (e[i].x == e[i].y) i --, m --;
} sort(e + 1, e + m + 1); Ans = 1e9;
F(i, 1, m) {
x = e[i].x, y = e[i].y, v[i + n] = e[i].b;
int xx = get(x), yy = get(y);
if (xx ^ yy) fat[xx] = yy; else {
int pos = Solve(x, y);
if (v[pos] <= e[i].b) continue;
cut(e[pos - n].x, pos), cut(pos, e[pos - n].y);
}
link(x, i + n), link(i + n, y);
if (get(1) == get(n))
Ans = min(Ans, e[i].a + v[Solve(1, n)]);
}
printf("%d\n", Ans == 1e9 ? - 1 : Ans);
}

3754. 【NOI2014】魔法森林(LCT)的更多相关文章

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

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

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

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

  3. [NOI2014]魔法森林 LCT

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

  4. loj2245 [NOI2014]魔法森林 LCT

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

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

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

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

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

  7. bzoj 3669: [Noi2014] 魔法森林 LCT版

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

  8. BZOJ 3669: [Noi2014]魔法森林 [LCT Kruskal | SPFA]

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

  9. P2387 [NOI2014]魔法森林 LCT维护最小生成树

    \(\color{#0066ff}{ 题目描述 }\) 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 ...

  10. 洛谷P2387 [NOI2014]魔法森林(LCT)

    魔法森林 题目传送门 解题思路 把每条路按照\(a\)的值从小到大排序.然后用LCT按照b的值维护最小生成树,将边按照顺序放入.如果\(1\)到\(n\)有了一条路径,就更新最小答案.这个过程就相当于 ...

随机推荐

  1. ArcGIS API for JavaScript 与 npm 之例子运行

    下载官方的demo,4.7的,在终端里跑了一下,测试成功(未打包) 在测试中精简掉了不需要的文件,使用http协议跑的(file协议不行哦) 最简单的目录如下: 我把以上文件放到一个叫demo的文件夹 ...

  2. solr8.0 ik中文分词器的简单配置(二)

    下载ik分词器,由于是solr8.0,一些ik分词器版本可能不兼容,以下是个人亲测可行的版本 ik分词器下载 然后将解压出来的两个jar包放到以下路径: 其它的三个文件放到以下路径: 如果没有clas ...

  3. Android 简单统计文本文件字符数、单词数、行数Demo

    做的demo是统计文本文件的字符数.单词数.行数的,首先呢,我们必须要有一个文本文件.所以我们要么创建一个文本文件,并保存,然后再解析:要么就提前把文本文件先放到模拟器上,然后检索到文本名再进行解析. ...

  4. Android Studio项目用Git上传至码云(OSChina)

    工具: Git安装(官网下载,默认安装即可). 步骤一: 新建一个Android Studio项目: 步骤二: 申请OSChina账号.登陆并创建新项目 1. 2. 3.点击复制,下面在Android ...

  5. 一起学Android之Layout

    本文简述在Android开发中布局的简单应用,属于基础知识,仅供学习分享使用. 概述 在Android UI开发中,布局类型主要有两种:LinearLayout(线性布局)和RelativeLayou ...

  6. 第六篇Scrum冲刺博客

    一.Daily Scrum Meeting照片 二.每个人的工作 成员 ItemID 已完成工作 明天计划完成的工作 遇到的困难 张鸿 o1 已完成工作,实现积分变换,碰撞检测 将其他剩余功能进行整合 ...

  7. Storm入门(五)Twitter Storm如何保证消息不丢失

    转自:http://xumingming.sinaapp.com/127/twitter-storm如何保证消息不丢失/ storm保证从spout发出的每个tuple都会被完全处理.这篇文章介绍st ...

  8. nginx 常见正则匹配符号表示

    1.^: 匹配字符串的开始位置: 2. $:匹配字符串的结束位置: 3..*: .匹配任意字符,*匹配数量0到正无穷: 4.\. 斜杠用来转义,\.匹配 . 特殊使用方法,记住记性了: 5.(值1|值 ...

  9. 如何在本地测试Fabric Code

    前一篇博客讲到了如何编译本地的Fabric Code成镜像文件,那么如果我们想改Fabric源代码,实现一些Fabric官方并没有提供的功能,该怎么办呢?这时我们除了改源码,增加需要的功能外,还需要能 ...

  10. codeforces#1152C. Neko does Maths(最小公倍数)

    题目链接: http://codeforces.com/contest/1152/problem/C 题意: 给出两个数$a$和$b$ 找一个$k(k\geq 0)$得到最小的$LCM(a+k,b+k ...