题目描述

  给你一个\(n\)个点\(m\)条边的图,每条边有两个边权\(a,b\)。请你找出从\(1\)到\(n\)一条路径,使得这条路径上边权\(a\)的最大值\(+\)边权\(b\)的最大值最小。

  \(n\leq 50000,m\leq 100000\)

题解

  我们可以考虑求出当边权\(a\leq\)某个数时边权\(b\)的最大值。

  先把边按边权\(a\)从小到大排序,依次加入,用LCT维护当前边权\(b\)的最小生成树。如果这两个点已经联通,就判断这两个点路径上边的边权\(b\)的最大值,如果大于当前这条边的边权\(b\),就把这条边删掉。否则就不加入这条边。

  每加完一条边我们就可以认为从\(1\)到\(n\)的边权\(a\)的最大值为当前这条边的边权\(a\)(否则就会在之前更新到),然后查询\(1\)到\(n\)的边权\(b\)的最大值,更新答案。

  时间复杂度:\(O(m\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace lct
{
int a[200010][2];
int f[200010];
pii v[200010];
pii s[200010];
int r[200010];
int root(int x)
{
return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
}
void reverse(int x)
{
swap(a[x][0],a[x][1]);
r[x]^=1;
}
void push(int x)
{
if(r[x])
{
if(a[x][0])
reverse(a[x][0]);
if(a[x][1])
reverse(a[x][1]);
r[x]=0;
}
}
void mt(int x)
{
s[x]=max(v[x],max(s[a[x][0]],s[a[x][1]]));
}
void rotate(int x)
{
if(root(x))
return;
int p=f[x];
int q=f[p];
int ps=(x==a[p][1]);
int qs=(p==a[q][1]);
int ch=a[x][ps^1];
if(!root(p))
a[q][qs]=x;
a[x][ps^1]=p;
a[p][ps]=ch;
if(ch)
f[ch]=p;
f[p]=x;
f[x]=q;
mt(p);
mt(x);
}
void clear(int x)
{
if(!root(x))
clear(f[x]);
push(x);
}
void splay(int x)
{
clear(x);
int p,q;
while(!root(x))
{
p=f[x];
if(!root(p))
{
q=f[p];
if((p==a[q][1])==(x==a[p][1]))
rotate(p);
else
rotate(x);
}
rotate(x);
}
}
void access(int x)
{
int y=x,t=0;
while(x)
{
splay(x);
a[x][1]=t;
mt(x);
t=x;
x=f[x];
}
splay(y);
}
void change(int x)
{
access(x);
reverse(x);
}
int findroot(int x)
{
access(x);
while(a[x][0])
x=a[x][0];
splay(x);
return x;
}
pii query(int x,int y)
{
change(x);
access(y);
return s[y];
}
void link(int x,int y)
{
change(x);
f[x]=y;
}
void cut(int x,int y)
{
change(x);
access(y);
f[a[y][0]]=0;
a[y][0]=0;
mt(y);
}
}
struct edge
{
int x,y;
int a,b;
};
edge a[100010];
int cmp(edge a,edge b)
{
return a.a<b.a;
}
int main()
{
// freopen("bzoj3669.in","r",stdin);
// freopen("bzoj3669.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=m;i++)
scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].a,&a[i].b);
sort(a+1,a+m+1,cmp);
int ans=0x7fffffff;
for(i=1;i<=m;i++)
{
if(a[i].x==a[i].y)
continue;
if(lct::findroot(a[i].x)==lct::findroot(a[i].y))
{
pii s=lct::query(a[i].x,a[i].y);
if(a[i].b>=a[s.second].b)
continue;
lct::cut(s.second+n,a[s.second].x);
lct::cut(s.second+n,a[s.second].y);
}
lct::v[i+n]=pii(a[i].b,i);
lct::link(a[i].x,i+n);
lct::link(a[i].y,i+n);
if(lct::findroot(1)==lct::findroot(n))
ans=min(ans,a[i].a+lct::query(1,n).first);
}
if(ans==0x7fffffff)
ans=-1;
printf("%d\n",ans);
return 0;
}

【BZOJ3669】【NOI2014】魔法森林 LCT的更多相关文章

  1. bzoj3669: [Noi2014]魔法森林 lct版

    先上题目 bzoj3669: [Noi2014]魔法森林 这道题首先每一条边都有一个a,b 我们按a从小到大排序 每次将一条路劲入队 当然这道题权在边上 所以我们将边化为点去连接他的两个端点 当然某两 ...

  2. bzoj3669: [Noi2014]魔法森林 lct

    记得去年模拟赛的时候好像YY出二分答案枚举a,b的暴力,过了55欸 然后看正解,为了将两维变成一维,将a排序,模拟Kruskal的加边过程,同时维护1到n的最大值,加入一条边e(u,v,a,b)时有以 ...

  3. [bzoj3669][Noi2014]魔法森林——lct

    Brief description 给定一个无向图,求从1到n的一条路径使得这条路径上最大的a和b最小. Algorithm Design 以下内容选自某HN神犇的blog 双瓶颈的最小生成树的感觉, ...

  4. BZOJ 3669: [Noi2014]魔法森林( LCT )

    排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...

  5. bzoj 3669: [Noi2014]魔法森林 (LCT)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec  ...

  6. [NOI2014]魔法森林 LCT

    题面 [NOI2014]魔法森林 题解 一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考 ...

  7. loj2245 [NOI2014]魔法森林 LCT

    [NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...

  8. 【BZOJ3669】[Noi2014]魔法森林 LCT

    终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...

  9. BZOJ3669[Noi2014]魔法森林——kruskal+LCT

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

  10. BZOJ3669: [Noi2014]魔法森林(瓶颈生成树 LCT)

    Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 3558  Solved: 2283[Submit][Status][Discuss] Descript ...

随机推荐

  1. Flask序列化

    我们在做后台接口的时候,对于返回值,用的最多的就是json数据格式 flask中,返回json数据格式,我们可以用到flask的jsonify函数. 对于基础序列是可以直接序列化的,但是更多的情况下, ...

  2. Oracle 一个用户将表权限赋给另一个用户

    原文地址:https://blog.csdn.net/u012129031/article/details/76218764 1.将用户user1的表权限赋给用户user2 select   'gra ...

  3. 周末时间学习Linux

    大家都是如何度过周末时光的呢?好多人都认为一周的工作后要好好休息下,于是在家疯狂的补觉,刷剧,打游戏,自我觉得很是正常,工作几天了,休息下不是当然嘛.是的,休息下很正常,但是把周末的时光都用到这些东西 ...

  4. Day5 Pyhton基础之编码与解码(四)

    1.编码与解码 1.1现在常用的编码类型

  5. Survey项目总结

    1.Ioc深入理解 Inverse of control org.springframework.scheduling.quartz.SchedulerFactoryBean org.mybatis. ...

  6. 不能再忽视了!宝宝不肯吃粥的N个原因,你避免了几个?

    辅食不懂怎么添加? 宝宝吃饭爱挑食? 营养均衡和多样化的辅食 在这里你都能找到 宝宝辅食微课堂 不能再忽视了!宝宝不肯吃粥的N个原因,你避免了几个? 2017-10-09 09:35 辅食不懂怎么添加 ...

  7. 简要了解 MySql 5.5/5.6/5.7/8 出现的新特性

    MySQL的开发周期 在比较之前,首先提一下MySQL的开发周期. MySQL一个大版本的开发,大致经历如下几个阶段: Feature Development Feature Testing Perf ...

  8. [转帖]linux 清空history以及记录原理

    linux 清空history以及记录原理 自己的linux 里面总是一堆 乱七八槽输错的命令 用这个办法 可以清空 linux的内容. 清爽一些. 1.当前session执行的命令,放置缓存中,执行 ...

  9. 逻辑斯特回归tensorflow实现

    calss #!/usr/bin/python2.7 #coding:utf-8 from __future__ import print_function import tensorflow as ...

  10. 【Java基础】switch语句实现根据数字输出对应星期

    代码: import java.util.Scanner; /* * switch语句格式: * switch(表达式) { * case 值1: * 语句体1; * break; * case 值2 ...