传送门

如果一条边只要考虑 $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. Spring中使用Velocity模板

    使用Velocity模板 Velocity是一种针对Java应用的易用的模板语言.Velocity模板中没有任何 Java代码,这使得它能够同时被非开发人员和开发人员轻松地理解.Velocity的用户 ...

  2. c语言打印空白星号矩形

    用户输入一个数字N,输出一个N*N的空心矩形,N最小为3 效果如下: 思路是这样的,首先拿到这道题是没有思路的,但我们可以举几个例子,当N等于3的情况,当N=5的情况,发现第一行和最后一行是相同的,而 ...

  3. asp.net服务器推送长连接

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx. ...

  4. SEO网站结构优化

    结构布局优化:用扁平化结构(层次结构超过三层小蜘蛛就不愿意爬了) 控制首页链接数量(中小网站100以内,页面导航.底部导航.锚文字链接等) 扁平化的目录层次(小蜘蛛跳转3次可以到达网站内任何一个内页, ...

  5. Request[]与Request.Params[] 差别

    Request[]与Request.Params[] ,这二个属性都可以让我们方便地根据一个KEY去[同时搜索]QueryString.Form.Cookies 或 ServerVariables这4 ...

  6. vmware10安装Arch

    必须保证机器能够上网! 1.vmware10中创建虚拟机(跟虚拟机中安装其他系统同样的操作). 2.开启上一步中创建的虚拟机. 3选择第一项进入 4自动进入root命令行 5进入 /dev (进行分区 ...

  7. JVM调用类的main方法的过程

    jvm先装载类,再链接类,再初始化类(以代码的文本顺序执行类变量初始化器.类静态初始化方法或接口的属性(field)初始化器),完成类的初始化后,才执行类的main方法.在链接过程中,可以静态链接(解 ...

  8. [转]-webkit-overflow-scrolling:touch的应用

    -webkit-overflow-scrolling 用来控制元素在移动设备上是否使用滚动回弹效果. 在移动端上,在你用overflow-y:scorll属性的时候,你会发现滚动的效果很木,很慢,这时 ...

  9. c# Include 与 用户控件

    <!-- #Include File="~/App_UC/head.bootstrap.aspx --> 这个路径文件可以是你html代码,也可以是应用脚本文件, 原理:跟用户控 ...

  10. "ServiceStack.Redis.RedisNativeClient”的方法“get_Db”没有实现。

    解决办法: 1.首先通过nuget程序包管理器将相关依赖项卸载干净 2.检查各项目模块中的package.config里还有没有redis的节点,如果已经存在就删除掉 3.去别的正常的项目中看一下re ...