正题

题目链接:https://www.ybtoj.com.cn/contest/114/problem/1


题目大意

给出\(n\)个点\(m\)条边的一张无向图,对于每个点\(i\)求不经过\(i\sim 1\)的最短路的第一条边的情况下\(i\)到\(1\)的最短路

数据保证这条边唯一

\(n\in[1,10^5],m\in[1,2\times 10^5],c\in[1,10^3]\)


解题思路

因为保证的那个东西,所以图的最短路树真的是一棵树了,所以先跑出最短路树考虑在最短路树上面搞。

然后题目限制了我们不能从树上的祖先那条边过来,这样就分为了两种情况。一种是从该点的子树外面连过来的边,另一种是从子树中走上来的边。第二种很麻烦,因为子树的最短路是用该节点的最短路扩展的,所以不能直接使用。

考虑一条非树边\((x,y)\),这条边会扩展一条\(dis_y+w\)到\(x\)的路径。(\(dis_x\)表示\(1\sim x\)的最短路)。

并且这条边可以使用到\(LCA(x,y)\)处,此时\(x\)的祖先们都不包含\(y\)在子树内,可以直接用\(y\)的子树扩展。

所以可以维护一个左偏树,每次合并两个儿子的信息,如果堆顶的边需要被删除就删除。需要写一个\(lazy\)标记来修改整棵树

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


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N=4e5+10;
struct point{
int val,x,y;
point(int v=0,int xx=0,int yy=0)
{val=v;x=xx;y=yy;return;}
};
bool operator<(point x,point y)
{return x.val<y.val;}
struct Heap{
point val[N];
int t[N][2],lazy[N],dis[N];
void Downdata(int x){
if(!lazy[x])return;
int ls=t[x][0],rs=t[x][1];
lazy[ls]+=lazy[x];lazy[rs]+=lazy[x];
val[ls].val+=lazy[x];val[rs].val+=lazy[x];
lazy[x]=0;return;
}
int Merge(int x,int y){
Downdata(x);Downdata(y);
if(!x||!y)return x+y;
if(val[y]<val[x])swap(x,y);
int &ls=t[x][0],&rs=t[x][1];
rs=Merge(rs,y);
if(dis[rs]>dis[ls])swap(ls,rs);
dis[x]=dis[rs]+1;return x;
}
int Del(int x){
int &ls=t[x][0],&rs=t[x][1];val[x]=0;
return Merge(ls,rs);
}
}T;
struct node{
int to,next,w;
}a[N];
int n,m,tot,cnt,num,ls[N],f[N];
int rfn[N],p[N],ans[N];
bool v[N];vector<int> G[N];
priority_queue<pair<int,int> >q;
void addl(int x,int y,int w){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;a[tot].w=w;
return;
}
void dij(){
memset(f,0x3f,sizeof(f));
q.push(mp(0,1));f[1]=0;
while(!q.empty()){
int x=q.top().second;q.pop();
if(v[x])continue;v[x]=1;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(f[x]+a[i].w<f[y]){
f[y]=f[x]+a[i].w;
q.push(mp(-f[y],y));
}
}
}
return;
}
void dfs(int x){
rfn[x]=++cnt;
for(int i=0;i<G[x].size();i++){
int y=G[x][i];dfs(y);
T.val[p[y]].val+=f[y]-f[x];
T.lazy[p[y]]+=f[y]-f[x];
p[x]=T.Merge(p[x],p[y]);
}
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(f[x]+a[i].w==f[y])continue;
if(f[y]+a[i].w==f[x])continue;
T.val[++num]=point(f[y]+a[i].w,x,y);
p[x]=T.Merge(p[x],num);
}
while(1){
if(!p[x]){ans[x]=-1;break;}
point w=T.val[p[x]];
if(rfn[w.y]>=rfn[x])
{p[x]=T.Del(p[x]);continue;}
ans[x]=w.val;break;
}
return;
}
int main()
{
freopen("pal.in","r",stdin);
freopen("pal.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
addl(x,y,w);addl(y,x,w);
}
dij();
for(int x=1;x<=n;x++)
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(f[x]+a[i].w==f[y])
G[x].push_back(y);
}
dfs(1);
for(int i=2;i<=n;i++)
if(!ans[i])puts("-1");
else printf("%d\n",ans[i]);
return 0;
}

YbtOJ#631-次短路径【左偏树,最短路】的更多相关文章

  1. 左偏树(Leftist Heap/Tree)简介及代码

    左偏树是一种常用的优先队列(堆)结构.与二叉堆相比,左偏树可以高效的实现两个堆的合并操作. 左偏树实现方便,编程复杂度低,而且有着不俗的效率表现. 它的一个常见应用就是与并查集结合使用.利用并查集确定 ...

  2. 【BZOJ 1367】 1367: [Baltic2004]sequence (可并堆-左偏树)

    1367: [Baltic2004]sequence Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Ou ...

  3. 『左偏树 Leftist Tree』

    新增一道例题 左偏树 Leftist Tree 这是一个由堆(优先队列)推广而来的神奇数据结构,我们先来了解一下它. 简单的来说,左偏树可以实现一般堆的所有功能,如查询最值,删除堆顶元素,加入新元素等 ...

  4. 洛谷.3273.[SCOI2011]棘手的操作(左偏树)

    题目链接 还是80分,不是很懂. /* 七个操作(用左偏树)(t2表示第二棵子树): 1.合并:直接合并(需要将一个t2中原有的根节点删掉) 2.单点加:把这个点从它的堆里删了,加了再插入回去(有负数 ...

  5. HDU 1512 Monkey King(左偏树模板题)

    http://acm.hdu.edu.cn/showproblem.php?pid=1512 题意: 有n只猴子,每只猴子一开始有个力量值,并且互相不认识,现有每次有两只猴子要决斗,如果认识,就不打了 ...

  6. luogu3261 懒惰左偏树 [JLOI2015]城池攻占

    目录 题目 思路 错误&&反思 代码 题目 luogu 原来左偏树真的能懒惰下放 那这篇博客应该要咕咕了 一开始我按照那篇博客想了一下,感觉emm,还是瞄了一眼看到了pushdown ...

  7. 1455: 罗马游戏[左偏树or可并堆]

    1455: 罗马游戏 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1861  Solved: 798[Submit][Status][Discuss] ...

  8. 洛谷 - P1552 - 派遣 - 左偏树 - 并查集

    首先把这个树建出来,然后每一次操作,只能选中一棵子树.对于树根,他的领导力水平是确定的,然后他更新答案的情况就是把他子树内薪水最少的若干个弄出来. 问题在于怎么知道一棵子树内薪水最少的若干个分别是谁. ...

  9. 洛谷 - P3377 - 【模板】左偏树(可并堆) - 左偏树 - 并查集

    https://www.luogu.org/problemnew/show/P3377 左偏树+并查集 左偏树维护两个可合并的堆,并查集维护两个堆元素合并后可以找到正确的树根. 关键点在于删除一个堆的 ...

随机推荐

  1. 《深入浅出vue.js》阅读笔记之(object)变化侦测

    1.什么是变化侦测? 通常,在运行时应用内部的状态会不断发生变化,此时需要不停地重新渲染页面,这时如何确定状态中发生了什么变化? 变化侦测就是用来解决这个问题的,它分为两种类型,一种是"推& ...

  2. WPF : ControlTemplate和DataTemplate的区别

    ControlTemplate用于描述控件本身. 使用TemplateBinding来绑定控件自身的属性, 比如{TemplateBinding Background}DataTemplate用于描述 ...

  3. springboot中@Mapper和@Repository的区别

    @Mapper和@Repository是常用的两个注解,两者都是用在dao上,两者功能差不多,容易混淆,有必要清楚其细微区别: 区别: @Repository需要在Spring中配置扫描地址,然后生成 ...

  4. git新建分支及提交代码到分支

    二.创建分支并提交代码到分支 上述添加成员的方式非常简单,但是如果说每一个小组成员都可以对仓库push内容,就涉及到一个代码的安全和冲突问题了,当多个成员同时在线编辑时容易出现冲突,假设A的代码是有问 ...

  5. 【java web】拦截器inteceptor

    一.简介 java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性. Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现 ...

  6. .net core api 对于FromBody的参数验证

    前言 在framework的mvc中,经常会使用 Model.State . ModelState.IsValid 配合着特性进行参数验证,通过这种方式可以降低controller的复杂度,使用方便. ...

  7. linux &和&&,|和||

    &和&&,|和||区别: &  表示任务在后台执行,如要在后台运行redis-server,则有  redis-server & && 表示前一 ...

  8. Spring Boot集成Redis集群(Cluster模式)

    目录 集成jedis 引入依赖 配置绑定 注册 获取redis客户端 使用 验证 集成spring-data-redis 引入依赖 配置绑定 注册 获取redis客户端 使用 验证 异常处理 同样的, ...

  9. Java反射的浅显理解

    一.回顾反射相关的知识 1.在xml文件中使用反射的好处: 1)代码更加灵活,后期维护只需要修改配置文件即可 · 初学者一般习惯于在代码本身上直接修改,后期也可以修改配置文件达到相同的目的 · 修改配 ...

  10. Java基础之SPI机制

    SPI 机制,全称为 Service Provider Interface,是一种服务发现机制.它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件 ...