[NOI2014]魔法森林 LCT
题面
题解
一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考虑一个暴力的做法。
一个暴力的做法:
我们枚举\(max(a[i])\),然后强制只能选满足这个限制的边,那么此时\(a[i]\)就已经不用管了,只需要最小化\(max(b[i])\)即可。
因此我们求一下最小生成树,然后看一下这条路径的\(max(b[i])\)就可以了。
一个小优化:
考虑如果我们枚举到一些不存在的\(max(a[i])\),显然是没用的,因此我们只需要枚举\(a[i]\)最大可以取到与哪条边的\(a[i]\)相等即可。
一个大优化:
注意到我们每枚举一次就重建最小生成树太亏了,毕竟我们有这样一个结论:
集合内多加一条边然后求最小生成树,这个新的最小生成树一定是在原来最小生成树基础上多加一条边构成的。
因此我们将新加入集合的边依次拿来尝试更新原来的最小生成树。
考虑怎么维护。
强行加入一条x到y的边后,原来的最小生成树将会变成一个基环树,因此我们要舍弃一条边。
我们在原来最小生成树上查询x到y路径上的最大值,看是否比当前加入的边大,如果比当前加入的边大,那么我们就断开那条最大的边,然后加入当前加入的边,否则就不做修改。
但是查询边的最大值不太好处理,我们可以将一条边视作一个点,然后连x ---> y 就连x ---> tmp ---> y,其中tmp为边的编号,然后断开的时候也断2条边。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 200000
#define inf 100000000
int n, m, ans = inf, top;
int maxn[AC], pos[AC], rev[AC], son[AC][2], fa[AC], val[AC];
int father[AC], q[AC];
struct node{
int x, y, a, b;
}way[AC];
inline bool cmp(node a, node b) {return a.a < b.a;}
inline int read()
{
int x = 0;char c = getchar();
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
inline void upmax(int &a, int b) {if(b > a) a = b;}
inline void upmin(int &a, int b) {if(b < a) a = b;}
inline int find(int x)
{
if(father[x] == x) return x;
else return father[x] = find(father[x]);
}
struct Link_Cut_Tree{
void pushdown(int x)
{
if(!rev[x]) return ;
rev[son[x][0]] ^= 1, rev[son[x][1]] ^= 1;
swap(son[x][0], son[x][1]), rev[x] = 0;//别忘了换儿子,之前还没换的
}
bool is_root(int x) {return (son[fa[x]][1] != x) && (son[fa[x]][0] != x);}
void update(int x)
{
int tmp = (maxn[son[x][0]] > maxn[son[x][1]]) ? son[x][0] : son[x][1];
if(val[x] > maxn[tmp]) maxn[x] = val[x], pos[x] = x;
else maxn[x] = maxn[tmp], pos[x] = pos[tmp];
}
void rotate(int x)
{
int y = fa[x], z = fa[y], k = son[y][1] == x;
if(!is_root(y)) son[z][son[z][1] == y] = x;
fa[x] = z, fa[son[x][k ^ 1]] = y, son[y][k] = son[x][k ^ 1];
fa[y] = x, son[x][k ^ 1] = y, update(y), update(x);
}
void splay(int x)
{
q[top = 1] = x;
for(R i = x; fa[i]; i = fa[i]) q[++ top] = fa[i];
while(top) pushdown(q[top]), -- top;
while(!is_root(x))
{
int y = fa[x], z = fa[y];
if(!is_root(y)) (son[y][1] == x) ^ (son[z][1] == y) ? rotate(x) : rotate(y);
rotate(x);
}
}
void access(int x)
{for(R i = 0; x; i = x, x = fa[x]) splay(x), son[x][1] = i, update(x);}
void make_root(int x) {access(x), splay(x), rev[x] ^= 1;}
void split(int x, int y) {make_root(x), access(y), splay(y);}
int get_rot(int x)
{
access(x), splay(x);
while(son[x][0]) x = son[x][0];
return x;
}
void link(int x, int y)
{
int xx = get_rot(x), yy = get_rot(y);
if(xx == yy) return ;
make_root(x), fa[x] = y;
}
void cut(int x, int y)
{
int xx = get_rot(x), yy = get_rot(y);
if(xx != yy) return ;
split(x, y);//split后默认y在最上面,x在y左边
if(!son[x][1] && !son[x][0]) son[y][0] = 0, fa[x] = 0;
}
void insert(int i)
{
int x = way[i].x, y = way[i].y, fx = find(x), fy = find(y);
if(fx != fy)
{
link(x, n + i), link(n + i, y);
if(fx > fy) swap(fx, fy);
father[fy] = fx;
}
else
{
split(x, y);
if(maxn[y] <= val[n + i]) return ;
int tmp = pos[y];
//son[tmp][0] = son[tmp][1] = fa[tmp] = 0;//把这个点删掉
cut(way[tmp - n].x, tmp), cut(tmp, way[tmp - n].y);//应该是断开和这条边相邻的点
link(x, n + i), link(n + i, y);
}
if(find(1) == find(n)) split(1, n), upmin(ans, way[i].a + maxn[n]);
}
}LCT;
void pre()
{
n = read(), m = read();
for(R i = 1; i <= n; i ++) father[i] = i;
for(R i = 1; i <= m; i ++)
way[i].x = read(), way[i].y = read(), way[i].a = read(), way[i].b = read();
sort(way + 1, way + m + 1, cmp);
}
void work()
{
for(R i = 1; i <= m; i ++) val[n + i] = way[i].b, LCT.insert(i);
if(ans != inf) printf("%d\n", ans);
else printf("-1\n");
}
int main()
{
freopen("in.in", "r", stdin);
pre();
work();
fclose(stdin);
return 0;
}
[NOI2014]魔法森林 LCT的更多相关文章
- BZOJ 3669: [Noi2014]魔法森林( LCT )
排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...
- bzoj 3669: [Noi2014]魔法森林 (LCT)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec ...
- loj2245 [NOI2014]魔法森林 LCT
[NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...
- bzoj3669: [Noi2014]魔法森林 lct版
先上题目 bzoj3669: [Noi2014]魔法森林 这道题首先每一条边都有一个a,b 我们按a从小到大排序 每次将一条路劲入队 当然这道题权在边上 所以我们将边化为点去连接他的两个端点 当然某两 ...
- 【BZOJ3669】[Noi2014]魔法森林 LCT
终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...
- bzoj 3669: [Noi2014] 魔法森林 LCT版
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ 3669: [Noi2014]魔法森林 [LCT Kruskal | SPFA]
题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…, ...
- P2387 [NOI2014]魔法森林 LCT维护最小生成树
\(\color{#0066ff}{ 题目描述 }\) 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 ...
- 洛谷P2387 [NOI2014]魔法森林(LCT)
魔法森林 题目传送门 解题思路 把每条路按照\(a\)的值从小到大排序.然后用LCT按照b的值维护最小生成树,将边按照顺序放入.如果\(1\)到\(n\)有了一条路径,就更新最小答案.这个过程就相当于 ...
随机推荐
- 高级PHP工程师所应该具备的专业素养
初次接触PHP,就为他的美所折服,于是一发不可收拾. 很多面试,很多人员能力要求都有“PHP高级工程师的字眼”,如果您真心喜欢PHP,并且您刚起步,那么我简单说说一个PHP高级工程师所应该具备的,希望 ...
- C++ 学习笔记 变量和基本类型(一)
C++ 学习笔记 一.变量和基本类型概述 类型是所有程序的基础.类型告诉我们数据代表什么意思以及可以对数据执行哪些操作. c++基本类型: 字符型 整型 浮点型 c++ 还提供了可用于自定义数据类型的 ...
- 基于Vue的简单通用分页组件
分页组件是每一个系统里必不可少的一个组件,分页组件分为两部分.第一部分是模版部分,用于显示当前分页组件的状态,例如正在获取数据.没有数据.没有下一页等等:第二部分是分页数据对象,用于封装一个分页组件的 ...
- idea_debug
条件断点 快捷键 cmd + shift +f8 demo 表达式求值 注意,调试的时候,选中相应变量 alt + f8 demo set value (感觉会非常有用) 调试时直接改变变量的值,快捷 ...
- 6.把建模工具导出的dea文件导入到three.js程序中
1.使用Three.js渲染导出的DAE 在Three.js中使用Collada(即.dae)文件的话,首先得要用到 ColladaLoader.js. 但是这个ColladaLoader.js并不包 ...
- 2018.4.23 linux系统
linux: 1.代表linux的内核 2.代表linux的操作系统:linux的内核和工具软件.应用软件..办公工具.开发工具. 它的特点: 1.它是开源软件,时当今最成功的开源软件之一.所以很多的 ...
- kali vmtools 不能复制粘贴解决方法(绝对实用)
朋友问起怎么vm kali 2019怎么不能复制了,而且网上的方法大多不适合.我就在这儿记录一笔吧,方便大家. 之前发现最新kali复制粘贴不能用,后来发现一个奇妙的套路,不是共享文件夹.只需要把文件 ...
- maven摘除jar包中配置文件
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-p ...
- python的Socket网络编程 使用模板
本文给出的是TCP协议的Socket编程. 其中用了一个dbmanager数据库操作模块,这个模块是我自己定义的,可以在我的另一个文章中找到这个模块的分享.python操作mysql数据库的精美实用模 ...
- 通过exp命令对Oracle数据库进行备份操作(提供两种情况的备份:备份本地,备份远程的数据库)
exp 用户名/密码@数据库所在ip地址:数据库端口号/数据库的service-name file=存储到的位置 这个是能成功的 http://www.2cto.com/database/201402 ...