BZOJ4699 树上的最短路(最短路径+dfs序+线段树+堆+并查集)
首先一般化的将下水道和塌陷看成一个东西。注意到在从源点出发的所有需要使用某条下水道的最短路径中,该下水道只会被使用一次,该下水道第一个被访问的点相同,且只会在第一个访问的点使用该下水道。这个第一个访问的点显然就是正常dij过程中,该下水道第一个被取出的点。
于是在dij过程中,取出了某个点进行更新后,就可以将所有起点包含它的边删掉了。但这样仍然没有复杂度保证,因为一条下水道会带来很多需要更新的点,且每个点会被重复更新多次。
那么可以想到稍微魔改一下dij,将点和下水道均入堆,其中下水道的权值是其第一个被访问的点的最短路+边权。如果某次从堆中取出的是下水道,使用其更新所有还未被取出的点,这样这条下水道已经发挥了所有功能可以被删掉,而所有其更新的点的最短路一定已被其确定,不需要再次更新,可以打上标记;如果从堆中取出的是点,使用其更新所有还未被使用的下水道即可,同理可以将该点删掉且下水道权值不会被再次更新。
最后的问题是如何实现这一过程。冷静一下,我们需要的操作只是找到一条下水道所连的所有未被标记的点、一个点所连的所有未被标记的下水道。前一个问题比较简单,可以在树上弄个并查集,维护其到根的路径上的第一个未被标记的点,查询时求个lca即可。后一个问题可以考虑枚举该点每棵子树,查询是否存在一端在该子树内一端在该子树外的路径,这可以用线段树维护一下dfs序区间的路径端点dfs序min和max,叶子处维护堆即可(或许用multiset会好写很多)。注意特殊处理一下一端恰为该节点的路径。
好像7k代码刷新了我的记录了啊?又丑又长还跑的慢。
#include<bits/stdc++.h>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 250010
#define M 600010
#define ll long long
int n,m,S,fa[N];
ll d[N];
struct road{int l1,r1,l2,r2,c;
}a[M];
struct data
{
int x,op;ll d;
bool operator <(const data&a) const
{
return d>a.d;
}
};
priority_queue<data> q;
vector<int> id;
namespace segment_tree
{
struct data1
{
int x,i;
bool operator <(const data1&a) const
{
return x<a.x||x==a.x&&i<a.i;
}
bool operator ==(const data1&a) const
{
return x==a.x&&i==a.i;
}
};
struct data2
{
int x,i;
bool operator <(const data2&a) const
{
return x>a.x||x==a.x&&i>a.i;
}
bool operator ==(const data2&a) const
{
return x==a.x&&i==a.i;
}
};
struct node
{
int L,R;data1 min;data2 max;
priority_queue<data2> ins_min,del_min;
priority_queue<data1> ins_max,del_max;
void update()
{
while (!del_min.empty()&&ins_min.top()==del_min.top()) ins_min.pop(),del_min.pop();
while (!del_max.empty()&&ins_max.top()==del_max.top()) ins_max.pop(),del_max.pop();
min=(data1){ins_min.top().x,ins_min.top().i};
max=(data2){ins_max.top().x,ins_max.top().i};
}
void ins(int x,int i){ins_min.push((data2){x,i}),ins_max.push((data1){x,i}),update();}
void del(int x,int i){del_min.push((data2){x,i}),del_max.push((data1){x,i}),update();}
}tree[N<<];
void up(int k){tree[k].min=min(tree[k<<].min,tree[k<<|].min),tree[k].max=min(tree[k<<].max,tree[k<<|].max);}
void build(int k,int l,int r)
{
tree[k].L=l,tree[k].R=r;
tree[k].min=(data1){N,},tree[k].max=(data2){,};
if (l==r)
{
tree[k].ins_min.push((data2){N,}),
tree[k].ins_max.push((data1){,});
return;
}
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
data1 query_min(int k,int l,int r)
{
if (tree[k].L==l&&tree[k].R==r) return tree[k].min;
int mid=tree[k].L+tree[k].R>>;
if (r<=mid) return query_min(k<<,l,r);
else if (l>mid) return query_min(k<<|,l,r);
else return min(query_min(k<<,l,mid),query_min(k<<|,mid+,r));
}
data2 query_max(int k,int l,int r)
{
if (tree[k].L==l&&tree[k].R==r) return tree[k].max;
int mid=tree[k].L+tree[k].R>>;
if (r<=mid) return query_max(k<<,l,r);
else if (l>mid) return query_max(k<<|,l,r);
else return min(query_max(k<<,l,mid),query_max(k<<|,mid+,r));
}
void ins(int k,int p,int x,int i)
{
if (tree[k].L==tree[k].R) {tree[k].ins(x,i);return;}
int mid=tree[k].L+tree[k].R>>;
if (p<=mid) ins(k<<,p,x,i);
else ins(k<<|,p,x,i);
up(k);
}
void del(int k,int p,int x,int i)
{
if (tree[k].L==tree[k].R) {tree[k].del(x,i);return;}
int mid=tree[k].L+tree[k].R>>;
if (p<=mid) del(k<<,p,x,i);
else del(k<<|,p,x,i);
up(k);
}
}
using segment_tree::query_min;
using segment_tree::query_max;
using segment_tree::ins;
using segment_tree::del;
namespace tree
{
int p[N],t,fa[N][],deep[N],dfn[N],size[N],cnt;
struct data{int to,nxt;}edge[N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k)
{
size[k]=;dfn[k]=++cnt;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k][])
{
fa[edge[i].to][]=k;
deep[edge[i].to]=deep[k]+;
dfs(edge[i].to);
size[k]+=size[edge[i].to];
}
}
void build()
{
fa[][]=;deep[]=;dfs();
for (int j=;j<;j++)
for (int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
}
int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
for (int j=;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][];
}
int getfa(int k){return k==?:fa[k][];}
bool in(int x,int l,int r){return dfn[x]<l||dfn[x]>r;}
bool cross(int x,int l,int r){return in(a[x].l1,l,r)&&!in(a[x].r1,l,r)||in(a[x].r1,l,r)&&!in(a[x].l1,l,r);}
void push(int l,int r)
{
for (;;)
{
int u=query_min(,l,r).i;
if (cross(u,l,r)) id.push_back(u),del(,dfn[a[u].l1],dfn[a[u].r1],u),del(,dfn[a[u].r1],dfn[a[u].l1],u);
else break;
}
for (;;)
{
int u=query_max(,l,r).i;
if (cross(u,l,r)) id.push_back(u),del(,dfn[a[u].l1],dfn[a[u].r1],u),del(,dfn[a[u].r1],dfn[a[u].l1],u);
else break;
}
}
void get(int k)
{
id.clear();
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k][])
{
int x=edge[i].to,l=dfn[x],r=dfn[x]+size[x]-;
push(l,r);
}
for (;;)
{
int u=query_min(,dfn[k],dfn[k]).i;
if (u) id.push_back(u),del(,dfn[a[u].l1],dfn[a[u].r1],u),del(,dfn[a[u].r1],dfn[a[u].l1],u);
else break;
}
}
}
using tree::deep;
using tree::getfa;
using tree::dfn;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dijkstra()
{
q.push((data){S,,});
memset(d,,sizeof(d));
for (int i=;i<=n;i++) fa[i]=i;fa[S]=find(getfa(S));
while (!q.empty())
{
data x=q.top();q.pop();
if (x.op==)
{
d[x.x]=x.d;tree::get(x.x);
for (int i=;i<id.size();i++)
q.push((data){id[i],,x.d+a[id[i]].c});
}
else
{
int u=a[x.x].l2,v=a[x.x].r2,w=tree::lca(u,v);
for (u=find(u);deep[u]>=deep[w];u=find(u))
q.push((data){u,,x.d}),fa[u]=find(getfa(u));
for (v=find(v);deep[v]>=deep[w];v=find(v))
q.push((data){v,,x.d}),fa[v]=find(getfa(v));
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4699.in","r",stdin);
freopen("bzoj4699.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read(),S=read();
for (int i=;i<n;i++)
{
int x=read(),y=read(),z=read();
tree::addedge(x,y),tree::addedge(y,x);
m++,a[m].l1=a[m].r1=x,a[m].l2=a[m].r2=y,a[m].c=z;
m++,a[m].l1=a[m].r1=y,a[m].l2=a[m].r2=x,a[m].c=z;
}
for (int i=;i<=m-(n-)*;i++) a[i].l2=read(),a[i].r2=read(),a[i].l1=read(),a[i].r1=read(),a[i].c=read();
tree::build();segment_tree::build(,,n);
for (int i=;i<=m;i++)
ins(,dfn[a[i].l1],dfn[a[i].r1],i),
ins(,dfn[a[i].r1],dfn[a[i].l1],i);
dijkstra();
for (int i=;i<=n;i++) printf(LL,d[i]);
return ;
}
BZOJ4699 树上的最短路(最短路径+dfs序+线段树+堆+并查集)的更多相关文章
- 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)
P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...
- 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)
牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...
- [luoguP3178] [HAOI2015]树上操作(dfs序 + 线段树 || 树链剖分)
传送门 树链剖分固然可以搞. 但还有另一种做法,可以看出,增加一个节点的权值会对以它为根的整棵子树都有影响,相当于给整棵子树增加一个值. 而给以某一节点 x 为根的子树增加一个权值也会影响当前子树,节 ...
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- BZOJ1103 [POI2007]大都市meg dfs序 线段树
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...
- Codeforces Round #442 (Div. 2)A,B,C,D,E(STL,dp,贪心,bfs,dfs序+线段树)
A. Alex and broken contest time limit per test 2 seconds memory limit per test 256 megabytes input s ...
- HDU.5692 Snacks ( DFS序 线段树维护最大值 )
HDU.5692 Snacks ( DFS序 线段树维护最大值 ) 题意分析 给出一颗树,节点标号为0-n,每个节点有一定权值,并且规定0号为根节点.有两种操作:操作一为询问,给出一个节点x,求从0号 ...
- POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和)
POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和) 题意分析 卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果.卡卡很喜欢苹果.树上有N个节点,卡卡给他们编号1到N,根 ...
- dfs序线段树
dfs序+线段树,啥?如果在一棵树上,需要你修改一些节点和查询一些节点,如果直接dfs搜的话肯定超时,那用线段树?树结构不是区间啊,怎么用?用dfs序将树结构转化为一个区间,就能用线段树进行维护了. ...
随机推荐
- kubespray -- k8s集群dashboard 访问方式
1.参考这篇文章: https://github.com/kubernetes/dashboard/wiki/Creating-sample-user 创建用户 2.获取token 3.kubectl ...
- PMS-授权中心
概述 授权中心用基于角色的访问控制思想(RBAC-Role Based Access Control)来实现各个业务系统的功能权限及数据权限控制.功能权限是指能否进入页面及使用页面上的操作.数据权限控 ...
- b/s程序真的很方便部署吗
公共应用当然是web系统,这个不说,我说的是企业应用. 最近一些年在企业开发中都提倡web应用,仿佛winform可以结束了,但真的这样吗?最近几天的真实经历如下: 我们部门新开发了一套系统要上线,由 ...
- 2019 The 19th Zhejiang University Programming Contest
感想: 今天三个人的状态比昨天计院校赛的状态要好很多,然而三个人都慢热体质导致签到题wa了很多发.最后虽然跟大家题数一样(6题),然而输在罚时. 只能说,水题还是刷得少,看到签到都没灵感实在不应该. ...
- 七年一冠、IG牛13的背后是什么!
最近忙着看S8世界总决赛,博客荒废了近一个月,后续步入正轨. 2018年11月3日.S8世界总决赛.中国终于夺得了S系列赛的总冠军. “IG牛逼”也开始刷爆社交圈,对于在S3入坑的我来说,也弥补上 ...
- [UWP 自定义控件]了解模板化控件(5.1):TemplatePart vs. VisualState
1. TemplatePart vs. VisualState 在前面两篇文章中分别使用了TemplatePart及VisualState的方式实现了相同的功能,其中明显VisualState的方式更 ...
- 【nodejs】让nodejs像后端mvc框架(asp.net mvc )一样处理请求--控制器的声明定义和发现篇(3/8)
文章目录 前情概要 前面文章把路由已经介绍的差不多了,包括url映射,路由选择等.接下来讲一讲controller的一些基本规则 BaseController的所有代码都在这里拉.相当简单. 主要逻辑 ...
- CAD2020下载安装AutoCAD2020中文版下载地址+安装教程
AutoCAD2020中文版为目前最新软件版本,我第一时间拿到软件进行安装测试,确保软件正常安装且各项功能正常可以使用,立刻拿出来分享,想用最新版本的话,抓紧下载使用吧: 我把我用的安装包贡献给你下载 ...
- Gerrit日常维护记录
Gerrit代码审核工具是个好东西,尤其是在和Gitlab和Jenkins对接后,在代码控制方面有着无与伦比的优势. 在公司线上部署了一套Gerrit系统,在日常运维中,使用了很多gerrit命令,在 ...
- Python_函数_复习_习题_24
# 函数 # 可读性强 复用性强# def 函数名(): # 函数体 #return 返回值# 所有的函数 只定义不调用就一定不执行 #先定义后调用 #函数名() #不接收返回值#返回值 = 函数名( ...