题解 P2387 【[NOI2014]魔法森林】
时间:2019-09-12
本文章向大家介绍题解 P2387 【[NOI2014]魔法森林】,主要包括题解 P2387 【[NOI2014]魔法森林】使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Solution[NOI204]魔法森林
题目大意:给定一个无向图,每条边有两个权值\(A\),\(B\),求一条从\(1\)到\(n\)的路径,使得\(max\{A\} + max\{B\}\)最小
分析:有两个限制不太好做,我们考虑枚举\(A\)的限制\(A_{limit}\)
每次把\(A \leq A_{limit}\)的边加入图中,然后我们只需要使得找一条路径使得\(max\{B\}\)最小,\(ans = A_{limit} + max\{B\}\)
再套个二分+并查集判连通性吧
以上为口胡算法,复杂度爆炸
然后我们来考虑优化:
首先枚举\(A_{limit}\),因为我们最后还不是限制了边的加入,所以可以直接把边按照\(A\)排序,然后加边
至于维护\(max\{B\}\),我们发现有些边是没有用的,我们只需要根据\(B\)贪心的加边,直到\(1\),\(n\)联通为止
听上去有点难以维护,但其实就是个\(Kruskal\)最小生成树.
然后问题就是动态加边维护\(MST\)
我们发现最多加入\(n-1\)条边后就一定会出现环,我们只需要把这条边加进去,把边权最大的边删掉即可
加边之前没有环是一颗森林,考虑动态树,我们就用\(LCT\)来维护吧主要是我只会这个
朴素\(LCT\)处理点权,我们用一个点来表示边,然后就是\(LCT\)模板了
代码:
#include <algorithm>
#include <cstdio>
#include <cctype>
using namespace std;
const int maxm = 1e5 + 100;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
namespace LCT{//LCT板子
#define ls ch[x][0]
#define rs ch[x][1]
const int maxn = 2e5;
int ch[maxn][2],faz[maxn],v[maxn],val_mx[maxn],rev[maxn];//注意:我们不仅要维护最大值,还要维护最大边权的边的编号,v是边权(点权),val_mx就是LCT维护的值
inline int isroot(int x){return ch[faz[x]][0] != x && ch[faz[x]][1] != x;}
inline int chk(int x){return ch[faz[x]][1] == x;}
inline void pushup(int x){//上传信息
val_mx[x] = x;
if(ls && v[val_mx[ls]] > v[val_mx[x]])val_mx[x] = val_mx[ls];
if(rs && v[val_mx[rs]] > v[val_mx[x]])val_mx[x] = val_mx[rs];
}
inline void pushr(int x)//一下均为模板{swap(ls,rs);rev[x] ^= 1;}
inline void pushdown(int x){
if(!rev[x])return;
if(ls)pushr(ls);
if(rs)pushr(rs);
rev[x] = 0;
}
inline void rotate(int x){
int y = faz[x],z = faz[y],k = chk(x),w = ch[x][!k];
ch[y][k] = w;if(w)faz[w] = y;
if(!isroot(y))ch[z][chk(y)] = x;faz[x] = z;
ch[x][!k] = y;faz[y] = x;
pushup(y),pushup(x);
}
inline void splay(int x){
static int st[maxn];
int y = x,top = 0;
st[++top] = y;
while(!isroot(y))st[++top] = y = faz[y];
while(top)pushdown(st[top--]);
while(!isroot(x)){
int y = faz[x];
if(!isroot(y))
chk(x) == chk(y) ? rotate(y) : rotate(x);
rotate(x);
}
}
inline void access(int x){
for(int y = 0;x;y = x,x = faz[x])
splay(x),ch[x][1] = y,pushup(x);
}
inline int findroot(int x){
access(x),splay(x);
while(ch[x][0])pushdown(x),x = ch[x][0];
splay(x);
return x;
}
inline void makeroot(int x){access(x),splay(x),pushr(x);}
inline void split(int x,int y){makeroot(x),access(y),splay(y);}
inline void link(int x,int y){makeroot(x);if(findroot(y) != x)faz[x] = y;}
inline void cut(int x,int y){makeroot(x);if(findroot(y) == x && faz[y] == x && !ch[y][0])faz[y] = ch[x][1] = 0,pushup(x);}
#undef ls
#undef rs
}using namespace LCT;
struct Edge{
int from,to,dista,distb,id;//id为原来编号
bool operator < (const Edge &rhs)const{
return (dista == rhs.dista) ? (distb < rhs.distb) : (dista < rhs.dista);
}
}org[maxm],Edges[maxm];//org为原来边数组,排序后打乱顺序
int n,m,ans = 0x7fffffff;
int main(){
n = read(),m = read();
for(int i = 1;i <= m;i++){
Edges[i].from = read();
Edges[i].to = read();
Edges[i].dista = read();
val_mx[n + i] = n + i;
v[n + i] = Edges[i].distb = read();
Edges[i].id = i;
org[i] = Edges[i];
}
sort(Edges + 1,Edges + 1 + m);
for(int i = 1;i <= m;i++){
Edge &e = Edges[i];
if(e.from == e.to)continue;
if(findroot(e.from) == findroot(e.to)){
split(e.from,e.to);
int mx = val_mx[e.to];//得到最大边的编号,其实就是点的编号
if(e.distb > v[mx])continue;//如果加进去马上又要删掉还不如不加
cut(mx,org[mx - n].from);
cut(mx,org[mx - n].to);
}
link(n + e.id,e.from),link(n + e.id,e.to);
if(findroot(1) == findroot(n))ans = min(ans,Edges[i].dista + (split(1,n),v[val_mx[n]]));
}
return printf("%d\n",ans == 0x7fffffff ? -1 : ans),0;
}
原文地址:https://www.cnblogs.com/colazcy/p/11515153.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Vue用keep-alive实现页面缓存
- Mybatis源码笔记之浅析StatementHandler
- Spring之事务传播行为
- Js处理异步async,await
- Vue_Cli4.x使用px2rem + vant搭建移动端项目
- Html新特性contenteditable自定义富文本
- 如何优雅的处理Restful
- 微信SDK实现多张图片上传
- JsBase64位转换为blob上传到服务器
- 微信小程序可移动浮窗
- Vant实现省市区三级联动
- springboot+Druid+mybatis整合
- Vue_cli升级4.x版本搭建项目
- String系列之format方法
- Flutter使用StatefulWidget有状态组件累计添加数组