传送门

如果一条边只要考虑 $a$ 的限制,那么显然最小生成树

但是现在有 $a,b$ 两个限制,所以考虑按 $a$ 从小到大枚举边,动态维护 $b$ 的最小生成树

考虑新加入的一条边 $x,y$ ,如果 $x,y$ 不在一颗树上显然直接加入,如果在一棵树上,考虑原本树上 $x$ 到 $y$ 的路径上 $b$ 最大的边

如果比当前边大,那么就把原本那条边从最小生成树上删除,把新的边加进去

答案就在每次加边时更新就好了

这个东西显然直接 $LCT$ 维护,为了维护边权所以要把边权也看成点

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=4e5+,INF=1e9+;
int n,m,ANS=INF;
struct dat{
int x,y,a,b;
inline bool operator < (const dat &tmp) const {
return a<tmp.a;
}
}d[N];
int c[N][],fa[N],t[N],val[N];
//把边化成点后,t维护点权最大的点的编号,val[x]存点x的b值
//边的编号为n+1到n+m
bool rev[N];
inline void pushdown(int x)
{
if(!rev[x]||!x) return;
int &lc=c[x][],&rc=c[x][];
rev[x]=; swap(lc,rc);
if(lc) rev[lc]^=;
if(rc) rev[rc]^=;
}
inline void rever(int x) { rev[x]^=; pushdown(x); }
inline void pushup(int x)
{
t[x]=x;
if(val[ t[c[x][]] ] > val[t[x]]) t[x]=t[c[x][]];
if(val[ t[c[x][]] ] > val[t[x]]) t[x]=t[c[x][]];
}
inline bool notroot(int x) { return (c[fa[x]][]==x)|(c[fa[x]][]==x); }
inline void rotate(int x)
{
int y=fa[x],z=fa[y],d=(c[y][]==x);
if(notroot(y)) c[z][c[z][]==y]=x;
fa[x]=z; fa[y]=x; fa[c[x][d^]]=y;
c[y][d]=c[x][d^]; c[x][d^]=y;
pushup(y); pushup(x);
}
inline void push_rev(int x)
{
if(notroot(x)) push_rev(fa[x]);
else pushdown(x);
pushdown(c[x][]); pushdown(c[x][]);
}
inline void splay(int x)
{
push_rev(x);
while(notroot(x))
{
int y=fa[x],z=fa[y];
if(notroot(y))
{
if(c[y][]==x ^ c[z][]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
inline void access(int x)
{
for(int y=;x;y=x,x=fa[x])
splay(x),c[x][]=y,pushup(x);
}
inline void makeroot(int x) { access(x); splay(x); rever(x); }
inline int findroot(int x)
{
access(x); splay(x); pushdown(x);
while(c[x][]) pushdown(c[x][]),x=c[x][];
splay(x);
return x;
}
inline int split(int x,int y) { makeroot(x); access(y); splay(y); return t[y]; }//提取一段路径上点权最大的点的编号
inline void link(int x,int y) { makeroot(x); if(findroot(y)!=x) fa[x]=y; }
inline void cut(int x,int y)
{
makeroot(x);
if(findroot(y)!=x||fa[y]!=x||c[y][]) return;
c[x][]=fa[y]=; pushup(x);
}
inline void query(int a)//更新答案
{
if(findroot()==findroot(n))//如果在同一颗树上
{
int w=split(,n);
ANS=min(ANS,a+val[w]);
}
}
inline void insert(int i)//加入边
{
int x=d[i].x,y=d[i].y,a=d[i].a,b=d[i].b; bool flag=;
if(findroot(x)==findroot(y))//如果原本已经是一颗树
{
int w=split(x,y);
if(val[w]>b) cut(w,d[w-n].x),cut(w,d[w-n].y);//如果b更小才cut
else flag=;//否则不连边
}
if(flag) link(n+i,x),link(n+i,y),query(a);//连边并更新ANS
}
int main()
{
n=read(),m=read();
for(int i=;i<=m;i++)
d[i].x=read(),d[i].y=read(),d[i].a=read(),d[i].b=read();
sort(d+,d+m+);
for(int i=;i<=m;i++) val[n+i]=d[i].b;
for(int i=;i<=m;i++) insert(i);
printf("%d",ANS <1e9 ? ANS : -);
return ;
}

P2387 [NOI2014]魔法森林的更多相关文章

  1. P2387 [NOI2014]魔法森林(LCT)

    P2387 [NOI2014]魔法森林 LCT边权维护经典题 咋维护呢?边化为点,边权变点权. 本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树 加边后出现环咋办? splay维护最大边 ...

  2. 洛谷 P2387 [NOI2014]魔法森林 解题报告

    P2387 [NOI2014]魔法森林 题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2 ...

  3. 洛谷P2387 [NOI2014]魔法森林(lct维护最小生成树)

    题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…, ...

  4. P2387 [NOI2014]魔法森林 LCT维护最小生成树

    \(\color{#0066ff}{ 题目描述 }\) 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 ...

  5. 洛谷P2387 [NOI2014]魔法森林(LCT)

    魔法森林 题目传送门 解题思路 把每条路按照\(a\)的值从小到大排序.然后用LCT按照b的值维护最小生成树,将边按照顺序放入.如果\(1\)到\(n\)有了一条路径,就更新最小答案.这个过程就相当于 ...

  6. [Luogu P2387] [NOI2014]魔法森林 (LCT维护边权)

    题面 传送门:https://www.luogu.org/problemnew/show/P2387 Solution 这题的思想挺好的. 对于这种最大值最小类的问题,很自然的可以想到二分答案.很不幸 ...

  7. 洛谷P2387 [NOI2014]魔法森林(LCT,Splay)

    在XZY&XZZ巨佬的引领下,一枚蒟蒻终于啃动了这道题...... 这次还是第一次写LCT维护边权,还要化边为点,思路乱七八糟的,写起来也不顺手,还好调了许久终于AC啦. 贪心排序按一个关键字 ...

  8. luogu P2387 [NOI2014]魔法森林

    传送门 这题似乎不好直接做,可以考虑按照\(a_i\)升序排序,然后依次加边更新答案 具体实现方法是用lct维护当前的树,这里需要维护链上最大的\(b_i\).每次加一条边,如果加完以后没有环直接加, ...

  9. 洛谷P2387 [NOI2014]魔法森林(LCT)

    在XZY&XZZ巨佬的引领下,一枚蒟蒻终于啃动了这道题...... 这次还是第一次写LCT维护边权,还要化边为点,思路乱七八糟的,写起来也不顺手,还好调了许久终于AC啦. 贪心排序按一个关键字 ...

随机推荐

  1. Ubuntu14.04下安装glog

    下载原始代码编译 1. Clone Source Code  glog git clone https://github.com/google/glog 2. Install dependencies ...

  2. Linux cmus

    一.简介 CMus 是一款类似于MOC, Herrie 或 mp3blaster 的基于终端的音乐播放器,支持 Ogg Vorbis, FLAC, MP3, WAV, Musepack, WavPac ...

  3. 10.Execution failed with exit status: 3

    错误信息: insert overwrite table t_mobile_mid_use_p_tmp4_rcf select '201411' as month_id, a.prov_id, a.c ...

  4. Luogu 2403 [SDOI2010]所驼门王的宝藏

    BZOJ 1924 内存要算准,我MLE了两次. 建立$n + r + c$个点,对于一个点$i$的坐标为$(x, y)$,连边$(n + x, i)$和$(n + r + y, i)$,代表这一列和 ...

  5. App测试从入门到精通之UI测试

    UI(user interface用户界面)的简称.UI测试也是APP测试中需要考虑的一个层面.用户至上,这个太重要了.一个好的App在界面的UI层设计上应该要满足简洁.美观.大气(这个是自己感觉的哈 ...

  6. javascript总结7:算术运算符

    1  运算符: 加号+ 如果是数字类型的变量相加,那么结果为数字类型; 如果是非数字类型的变量相加,结果为字符串类型 2  减号- 如果是非数字类型的变量相减结果为  NaN 3 乘号 * 如果是非数 ...

  7. 再次迷宫救人——BFS

    原创 上次用DFS解了迷宫救人:https://www.cnblogs.com/chiweiming/p/9313164.html 这次用BFS(广度优先搜索),实现广度优先搜索比深度优先搜索复杂,思 ...

  8. jQuery事件(持续更新中)

    方法 描述 bind() 向匹配元素附加一个或更多事件处理器 blur() 触发.或将函数绑定到指定元素的 blur 事件 change() 触发.或将函数绑定到指定元素的 change 事件 cli ...

  9. SQLServer 附加数据库后只读或报错解决方法

    百度文库地址 http://wenku.baidu.com/link?url=3EnK52mOtll3svjce0OGUUu7h9EOWkUgty8VChkxRdX7LQlm9Ll6N_78ENngN ...

  10. rsync服务搭建--2018.5.8 [优化后最终版]

    2018年5月8日 22:09:38 第一步配置基础环境(按照自己的规划配置并非每人的环境都一致) 第一台服务器(RSYNC服务器): rsync外网地址:10.0.0.41  rsync内网地址:1 ...