[NOI2014] 魔法森林 - Link Cut Tree
[NOI2014] 魔法森林
Description
给定一张图,每条边 \(i\) 的权为 \((a_i,b_i)\), 求一条 \(1 \sim n\) 路径,最小化 \(\max_{i\in P}{a_i} + \max_{i\in P}{b_i}\)
Solution
如果我们限定最大的 \(b_i\) ,那么路径一定在以 \(a_i\) 为权的最小生成树上。
Proof. 考虑反证,假设这条路径中有一条边不在 MST 上,那么这条非树边与树必然形成一个环,很显然用这条边换掉环上任意一条其它边会减小生成树的权值,与最小生成树的条件矛盾。
利用这个性质构造算法。我们从小到大枚举 \(b_i\) 的最大值,依次将边加入树中,同时用 LCT 维护以 \(a_i\) 为边权的最小生成树即可。
维护最小生成树的方法非常简单。利用 LCT 维护树链最大值,当新边 \((u,v,w)\) 加入时,我们考虑如果这条边比树链 \(u \sim v\) 上的最大值小,那么就用它换掉那个最大值的边。因此 LCT 上需要维护最大值以及最大值的位置。
使用 LCT 维护边权通常的做法是对边建点,点权赋为边权,而真实的点则不具有点权。同时维护边的基本信息。
以下阐述为较为具体的实现方式
在本题中,我们可以用编号 \(1 \sim n\) 的点来代表真实点,用编号 \(n+1 \sim n+m\) 的点来代表边,同时在一个结构体数组中以 \((u,v,w)\) 的形式记录边权的基本信息。注意边的编号可以用按照 \(b_i\) 为关键字排序后的代替。
在 LCT 的实现层面上,我们可以姑且忽略这一切,全当作维护树链的最大点权和最大点权点编号。
当我们需要查询 \(u \sim v\) 间的最大边权时,我们只需要在 LCT 上询问即可。
当我们需要删除一条边的时候,我们只需要知道它的编号 \(i\) ,然后断开 \((u_i, n+i)\) 和 \((v_i, n+i)\)
当我们需要加入一条边的时候,我们只需要知道它的编号 \(i\) ,然后连接 \((u_i, n+i)\) 和 \((v_i, n+i)\)
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000000;
int n,m;
struct Edge {
int u,v,w,b;
bool operator < (const Edge &x) const {
return b < x.b;
}
} e[N];
struct LinkCutTree {
int top, q[N], ch[N][2], fa[N], rev[N], mx[N], mp[N], val[N];
inline void pushup(int x) {
mx[0]=mp[0]=0;
mx[x] = max(max(mx[ch[x][0]],mx[ch[x][1]]),val[x]);
if(mx[x] == mx[ch[x][0]])
mp[x]=mp[ch[x][0]];
if(mx[x] == mx[ch[x][1]])
mp[x]=mp[ch[x][1]];
if(mx[x] == val[x])
mp[x]=x;
}
inline void pushdown(int x) {
if(!rev[x])
return;
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
inline bool isroot(int x) {
return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
}
inline void rotate(int p) {
int q=fa[p], y=fa[q], x=ch[fa[p]][1]==p;
ch[q][x]=ch[p][x^1];
fa[ch[q][x]]=q;
ch[p][x^1]=q;
fa[q]=p;
fa[p]=y;
if(y)
if(ch[y][0]==q)
ch[y][0]=p;
else if(ch[y][1]==q)
ch[y][1]=p;
pushup(q);
pushup(p);
}
inline void splay(int x) {
q[top=1]=x;
for(int i=x; !isroot(i); i=fa[i])
q[++top]=fa[i];
for(int i=top; i; i--)
pushdown(q[i]);
for(; !isroot(x); rotate(x))
if(!isroot(fa[x]))
rotate((ch[fa[x]][0]==x)==(ch[fa[fa[x]]][0]==fa[x])?fa[x]:x);
}
void access(int x) {
for(int t=0; x; t=x,x=fa[x])
splay(x),ch[x][1]=t,pushup(x);
}
void makeroot(int x) {
access(x);
splay(x);
rev[x]^=1;
}
int find(int x) {
access(x);
splay(x);
while(ch[x][0])
x=ch[x][0];
return x;
}
void split(int x,int y) {
makeroot(x);
access(y);
splay(y);
}
void cut(int x,int y) {
split(x,y);
if(ch[y][0]==x)
ch[y][0]=0, fa[x]=0;
}
void link(int x,int y) {
makeroot(x);
fa[x]=y;
}
void setval(int p,int v) {
val[p]=mx[p]=v;
mp[p]=p;
}
int queryv(int p,int q) {
split(p,q);
return mx[q];
}
int queryp(int p,int q) {
split(p,q);
return mp[q];
}
} lct;
bool check(int p,int q) {
return lct.find(p)==lct.find(q);
}
int queryv(int p,int q) {
return lct.queryv(p,q);
}
int queryp(int p,int q) {
return lct.queryp(p,q)-n;
}
void link(int i) {
lct.link(n+i,e[i].u);
lct.link(n+i,e[i].v);
}
void cut(int i) {
lct.cut(n+i,e[i].u);
lct.cut(n+i,e[i].v);
}
int ans = 1e+12;
signed main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++) {
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].b);
}
sort(e+1,e+m+1);
for(int i=1;i<=n;i++) lct.setval(i, 0);
for(int i=1;i<=m;i++) lct.setval(i+n, e[i].w);
for(int i=1;i<=m;i++) {
if(check(e[i].u,e[i].v)) {
int v=queryv(e[i].u,e[i].v), p=queryp(e[i].u,e[i].v);
if(v > e[i].w) {
cut(p);
link(i);
}
}
else {
link(i);
}
if(check(1,n)) {
ans = min(ans, queryv(1,n) + e[i].b);
}
}
if(ans < (int)1e+12) cout<<ans<<endl;
else cout<<-1<<endl;
}
[NOI2014] 魔法森林 - Link Cut Tree的更多相关文章
- BZOJ 3669 [Noi2014]魔法森林 ——SPFA / Link-Cut Tree
[题目分析] 大意就是有一张图,边权有两个值,ai和bi 找到一条路径,使得路径上的max(ai)+max(bi)最小. 遇到有两个权值或者多个权值的时候,如果他们互相影响,试着用分块搞一搞. 如果互 ...
- 【BZOJ3669】【Noi2014】魔法森林(Link-Cut Tree)
[BZOJ3669][Noi2014]魔法森林(Link-Cut Tree) 题面 题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n ...
- link cut tree 入门
鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...
- Link Cut Tree 总结
Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...
- 学习笔记:Link Cut Tree
模板题 原理 类似树链剖分对重儿子/长儿子剖分,Link Cut Tree 也做的是类似的链剖分. 每个节点选出 \(0 / 1\) 个儿子作为实儿子,剩下是虚儿子.对应的边是实边/虚边,虚实时可以进 ...
- BZOJ_3669_[Noi2014]魔法森林_LCT
BZOJ_3669_[Noi2014]魔法森林_LCT Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节 ...
- bzoj 3669: [Noi2014]魔法森林 (LCT)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec ...
- 「luogu2387」[NOI2014] 魔法森林
「luogu2387」[NOI2014] 魔法森林 题目大意 \(n\) 个点 \(m\) 条边的无向图,每条边上有两个权值 \(a,b\),求从 \(1\) 节点到 \(n\) 节点 \(max\{ ...
- P2387 [NOI2014]魔法森林(LCT)
P2387 [NOI2014]魔法森林 LCT边权维护经典题 咋维护呢?边化为点,边权变点权. 本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树 加边后出现环咋办? splay维护最大边 ...
随机推荐
- Git 的 .gitignore 配置说明 (C#)
1.配置语法: 以斜杠“/”开头表示目录: 以星号“*”通配多个字符: 以问号“?”通配单个字符 以方括号“[]”包含单个字符的匹配列表: 以叹号“!”表示不忽略(跟踪)匹配到的文件或目录: 此外,g ...
- PAT甲级代码仓库
大道至简,知易行难.希望能够坚持刷题. PAT甲级真题题库,附上我的代码. Label Title Score Code Level 1001 A+B Format 20 1001 * 1002 A+ ...
- 制作MySQL RPM安装包Spec
适用环境: 数据库版本:MySQL 操作系统:CentOS 7 制作思路: 将数据库初始化和配置工作放到安装脚本中方便定制: 1.打包MySQL应用目录 2.不自动生成配置文件 3.不自动生成数据目录 ...
- 如何修复MacBook Pro过热:保持MacBook散热的13个技巧
尽管MacBook Pro是一台能处理繁重工作量的高能效机器,但它却无法像市场上其他笔记本电脑一样避免过热.至于什么可以防止发热,那不是一两个技巧就能解决的问题.相反,这是一组技巧可以解决的挑战. 因 ...
- Gitee Git bash VSCode操作简易说明
GIT Git是一个分布式的版本控制系统,只是软件,需要你下载装到电脑上,实现git功能. Github.Gitee基于git的项目托管平台.Github是国外的,连接速度因人而异:另外Github收 ...
- java文本文件加密
加密方法是通过输入流对源文件字符逐个读取,对其读取到字符的ascll值进行异或运算,并将其放入新文件中,解密时只要用相同的密钥进行ascll异或运算并向新文件输出即可,即对文件首次用该程序处理为加密, ...
- nodejs events
EventEmitter类 events模块提供一个对象:events.EventEmitter,核心是事件触发和事件监听的封装. 大多数时候不会直接使用EventEmitter,而是在对象中继承它( ...
- Selenium实战(三)——滑动解锁+窗口截图
一.简单滑块的实例 当单机滑块时,改变的只是CSS样式,HTML代码如下: <div class="slide-to-unlock-progress" style=" ...
- Linux内核提权漏洞(CVE-2019-13272)
漏洞描述 kernel / ptrace.c中的ptrace_link错误地处理了想要创建ptrace关系的进程的凭据记录,这允许本地用户通过利用父子的某些方案来获取root访问权限 进程关系,父进程 ...
- Git本地仓库的使用
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. Git 与 SVN 区别点: 1.Git 是分布式的,SVN 不是:这是 Git 和其它非分布式的版本控制系统,例如 S ...