☆ [NOI2014] 魔法森林 「LCT动态维护最小生成树」
题目类型:\(LCT\)动态维护最小生成树
传送门:>Here<
题意:带权无向图,每条边有权值\(a[i],b[i]\)。要求一条从\(1\)到\(N\)的路径,使得这条路径上的\(Max\{a[i]\}+Max\{b[i]\}\)最小
解题思路
\(LCT\)板子打错调试了半个小时……菜到不能再菜了……
首先我们发现题目要求不是最小的和,而是最小的\(Max\{a[i]\}+Max\{b[i]\}\)——都只取决于最大。因此我们可以联想,如果\(Max\{a\}\)确定了,那么余下的就是用所有权值\(a\)不超过\(Max\{a\}\)的边构建一棵以\(b\)为关键字的最小生成树。显然,这样一定能保证答案最优。
因此我们可以考虑将所有边以\(a\)为关键字从小到大排序,由于是从小到大排序的,所以\(Max\{a\}\)一定是最后加入生成树的这条边的\(a\)。余下的就是维护最小生成树了,那么直接用\(LCT\)进行动态维护就好了。如果\(1,N\)已经连通(利用\(findroot\)实现并查集),那么先\(split\),查询\(Max\{b\}\),加上目前为止最大的\(Max\{a\}\)更新答案即可。
非常巧妙~~
反思
要抓住题目所说的\(Max\)
另外,\(LCT\)的\(pushup\)中要更新三次,并且每次更新\(mx[x]\)都有可能改变,因此要用\(mx[x]\)而不是\(x\)。
Code
/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 50010;
const int MAXM = 100010;
const int MAXS = MAXN + MAXM;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
int x = 0; int w = 1; register char c = getchar();
for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
if(c == '-') w = -1, c = getchar();
for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
struct Edge{
int x,y,a,b;
}e[MAXM];
int N,M,ans(INF),maxa;
int val[MAXS],mx[MAXS];
struct LinkCutTree{
int ch[MAXS][2],fa[MAXS];
bool tag[MAXS];
inline bool rson(int f, int x){
return ch[f][1] == x;
}
inline bool isroot(int x){
return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
}
inline void pushup(int x){
mx[x] = x;
if(val[mx[ch[x][0]]] > val[mx[x]]) mx[x] = mx[ch[x][0]];
if(val[mx[ch[x][1]]] > val[mx[x]]) mx[x] = mx[ch[x][1]];
}
inline void rotate(int x){
int f = fa[x], gf = fa[f];
bool p = rson(f,x), q = !p;
if(!isroot(f)) ch[gf][rson(gf,f)] = x; fa[x] = gf;
ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
ch[x][q] = f, fa[f] = x;
pushup(f), pushup(x);
}
void reverse(int x){
if(!isroot(x)) reverse(fa[x]);
if(!tag[x]) return;
tag[x] = 0;
swap(ch[x][0], ch[x][1]);
tag[ch[x][0]] ^= 1, tag[ch[x][1]] ^= 1;
}
inline void splay(int x){
for(reverse(x); !isroot(x); rotate(x)){
if(isroot(fa[x])){ rotate(x); break; }
if(rson(fa[fa[x]],fa[x]) ^ rson(fa[x],x)) rotate(x); else rotate(fa[x]);
}
}
inline void access(int x){
for(int y = 0; x; y=x, x = fa[x]) splay(x), ch[x][1] = y, pushup(x);
}
inline void mroot(int x){
access(x), splay(x), tag[x] ^= 1;
}
inline int findroot(int x){
access(x), splay(x);
while(ch[x][0]) x = ch[x][0];
return x;
}
inline void split(int x, int y){
mroot(x);
access(y);
splay(y);
}
inline void link(int x, int y){
mroot(x);
fa[x] = y;
}
inline void cut(int x, int y){
split(x,y);
if(ch[y][0] != x || ch[x][1]) return;
ch[y][0] = fa[x] = 0;
}
}qxz;
inline bool cmp(const Edge& a, const Edge& b){
return a.a < b.a;
}
int main(){
// freopen(".in","r",stdin);
N = read(), M = read();
for(int i = 1; i <= M; ++i){
e[i].x = read(), e[i].y = read(), e[i].a = read(), e[i].b = read();
}
sort(e+1, e+M+1, cmp);
for(int i = 1; i <= M; ++i){
val[N+i] = e[i].b;
if(qxz.findroot(e[i].x) != qxz.findroot(e[i].y)){
qxz.link(e[i].x, N+i);
qxz.link(e[i].y, N+i);
maxa = e[i].a;
}
else{
qxz.split(e[i].x, e[i].y);
if(val[mx[e[i].y]] > e[i].b){
int temp = mx[e[i].y]-N;
qxz.cut(e[temp].x, temp+N);
qxz.cut(e[temp].y, temp+N);
qxz.link(e[i].x, N+i);
qxz.link(e[i].y, N+i);
maxa = e[i].a;
}
}
if(qxz.findroot(1) == qxz.findroot(N)){
qxz.split(1, N);
ans = Min(ans, maxa + val[mx[N]]);
}
}
if(ans == INF) printf("-1");
else printf("%d", ans);
return 0;
}
☆ [NOI2014] 魔法森林 「LCT动态维护最小生成树」的更多相关文章
- ☆ [WC2006] 水管局长 「LCT动态维护最小生成树」
题目类型:\(LCT\)动态维护最小生成树 传送门:>Here< 题意:给出一张简单无向图,要求找到两点间的一条路径,使其最长边最小.同时有删边操作 解题思路 两点间路径的最长边最小,也就 ...
- P2387 [NOI2014]魔法森林(LCT)
P2387 [NOI2014]魔法森林 LCT边权维护经典题 咋维护呢?边化为点,边权变点权. 本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树 加边后出现环咋办? splay维护最大边 ...
- 洛谷P2387 [NOI2014]魔法森林(lct维护最小生成树)
题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…, ...
- 洛谷2387 NOI2014魔法森林(LCT维护最小生成树)
本题是运用LCT来维护一个最小生成树. 是一个经典的套路 题目中求的是一个\(max(a_i)+max(b_i)\)尽可能小的路径. 那么这种的一个套路就是,先按照一维来排序,然后用LCT维护另一维 ...
- 3669. [NOI2014]魔法森林【LCT 或 SPFA动态加边】
Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...
- BZOJ3669[Noi2014]魔法森林——kruskal+LCT
题目描述 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住 ...
- 【bzoj3669】[Noi2014]魔法森林 Kruskal+LCT
原文地址:http://www.cnblogs.com/GXZlegend/p/6797748.html 题目描述 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看 ...
- 【BZOJ】3669: [Noi2014]魔法森林(lct+特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, ...
- 洛谷P4234 最小差值生成树(lct动态维护最小生成树)
题目描述 给定一个标号为从 11 到 nn 的.有 mm 条边的无向图,求边权最大值与最小值的差值最小的生成树. 输入输出格式 输入格式: 第一行两个数 n, mn,m ,表示图的点和边的数量. ...
随机推荐
- 测者的性能测试手册:快速安装LoadRunner Linux上的Generator
安装和初始化 安装包 上传Linux.zip(LoadRunner Generator for Linux.zip,后台回复loadrunner获取下载地址),然后通过如下命令: unzip Linu ...
- Linux内核高端内存
Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图. Linux内核地址空间划分 通常32位L ...
- c/c++ 继承与多态 容器与继承2
c/c++ 继承与多态 容器与继承1说明了容器里使用继承关系的方法,这里再弄一个练习,巩固一下. 做一个类Basket,它有个multiset成员,key是智能指针std::shared_ptr< ...
- 我认知的javascript之函数调用
今天刚好周六没事,又由于工作的原因导致早上醒来就睡不着,无聊之下,就想到了 js 的function调用问题.当然,网上也是对javascript的一些事情说得很透了,但我觉得还是有必要把自己的想法说 ...
- 固态+机械双硬盘分别安装Win10和Ubuntu16.04双系统
博主的笔记本是256G固态+1T机械,固态事先已经安装好了Win10系统,想着把机械硬盘分出500G用来安装Ubuntu16.04,剩余的继续用作Win下的资料盘.这里不介绍安装Win10过程,也不记 ...
- 【Linux基础】查看硬件信息-系统
1.查看计算机名 hostname 2.查看内核/操作系统/CPU信息 uname -a 4.查看操作系统版本(Linux) head -n 2 /etc/issue Red Hat Enterp ...
- 数据可视化的开源方案: Superset vs Redash vs Metabase (二)
在上篇结尾处我提到“如果现在让我重新选择,我会使用哪个可视化工具?”我的答案是 Redash,原因主要不是功能层面,而是技术层面.本篇就从项目关注度与活跃度,项目的技术架构,源代码的规模与质量,这三个 ...
- 替换空格[by Python]
题目: 请实现一个函数,将一个字符串中的空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 1.使用python自带的repla ...
- Redis常用数据结构
Redis常用数据结构包括字符串(strings),列表(lists),哈希(hashes),集合(sets),有序集合(sorted sets). redis的key最大不能超过512M,可通过re ...
- c++stack类的用法
官方解释: LIFO stack Stacks are a type of container adaptor, specifically designed to operate in a LIFO ...