☆ [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 ,表示图的点和边的数量. ...
随机推荐
- android中的相对路径
转载请标明出处:https://www.cnblogs.com/tangZH/p/9939655.html 1.同个文件夹访问 D:\Java\main\A.java D:\Java\main\B. ...
- Android的WebView调试工具(无需Fan墙,可同时调试多个设备,永不过期)
缘起 前端开发离不开Chrome的开发者工具,尤其是调试Android WebView时.然而,如果使用chrome://Inspect的方法,国内的开发者会惊奇地发现“空白啊”!为此,我发布过这个离 ...
- Fragment与Activity的生命周期对比
因为fragment是依赖于activity的,所以activity的创建相关都是先于fragment的,fragment的销毁相关都是先于activity的.
- python闭包和装饰器
本文目录: 1. 闭包的解析和用法 2. 函数式装饰器 3. 类装饰器 一.闭包 闭包是一种函数,从形式上来说是函数内部定义(嵌套)函数,实现函数的扩展.在开发过程中,考虑到兼容性和耦合度问题,如果想 ...
- Edge BUG欣赏之四摸鸡与IP地址的恩怨
<html><head> <meta http-equiv="Content-Type" content="text/html; c ...
- 07-Vue的基础使用
vue的介绍 前端框架和库的区别 nodejs的简单使用 vue的起步 指令系统 组件的使用 过滤器的使用 watch和computed 钩子函数 渐进式的JavaScript框架 vue react ...
- 关于MongoDB数据库的日志解析
MongoDB日志记录了数据库实例的健康状态.语句的执行状况.资源的消耗情况,所以日志对于分析数据库服务和性能优化很有帮助. 因此,很有必要花费一些时间来学习解析一下MongoDB的日志文件. 日志信 ...
- Hexo自定义页面的方法
原文转自:http://refined-x.com/2017/07/10/Hexo%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A1%B5%E9%9D%A2%E7%9A%84%E6%9 ...
- 【vue】vue +element 搭建项目,mock模拟数据(纯干货)
1.安装mockjs依赖 (c)npm install mockjs --save-dev 2.安装axios(Ajax) (c)npm install --save axios 3.项目目录 4.设 ...
- 第五章:Realm
一,UserRealm 1,UserRealm父类AuthorizingRealm将获取Subject相关信息分成两步: 1.1,获取身份验证信息(doGetAuthenticationInfo): ...