首先一般化的将下水道和塌陷看成一个东西。注意到在从源点出发的所有需要使用某条下水道的最短路径中,该下水道只会被使用一次,该下水道第一个被访问的点相同,且只会在第一个访问的点使用该下水道。这个第一个访问的点显然就是正常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序+线段树+堆+并查集)的更多相关文章

  1. 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)

    P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...

  2. 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)

    牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...

  3. [luoguP3178] [HAOI2015]树上操作(dfs序 + 线段树 || 树链剖分)

    传送门 树链剖分固然可以搞. 但还有另一种做法,可以看出,增加一个节点的权值会对以它为根的整棵子树都有影响,相当于给整棵子树增加一个值. 而给以某一节点 x 为根的子树增加一个权值也会影响当前子树,节 ...

  4. BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树

    题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...

  5. BZOJ1103 [POI2007]大都市meg dfs序 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...

  6. 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 ...

  7. HDU.5692 Snacks ( DFS序 线段树维护最大值 )

    HDU.5692 Snacks ( DFS序 线段树维护最大值 ) 题意分析 给出一颗树,节点标号为0-n,每个节点有一定权值,并且规定0号为根节点.有两种操作:操作一为询问,给出一个节点x,求从0号 ...

  8. POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和)

    POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和) 题意分析 卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果.卡卡很喜欢苹果.树上有N个节点,卡卡给他们编号1到N,根 ...

  9. dfs序线段树

    dfs序+线段树,啥?如果在一棵树上,需要你修改一些节点和查询一些节点,如果直接dfs搜的话肯定超时,那用线段树?树结构不是区间啊,怎么用?用dfs序将树结构转化为一个区间,就能用线段树进行维护了. ...

随机推荐

  1. Android 如果布局中有ScrollView和Fragment或者带有滚动条的布局进行嵌套,布局加载完成页面无法定位到顶部的情况

    页面无法定位到顶部: 1.ScrollView中嵌套Fragment(Fragment中可能有想ScrollView,ListView带有滚动条的控件). 2.ScrollView嵌套ScrollVi ...

  2. 关于PCB开窗

    如果走220V,那么线宽一点,一般高电压下面不覆铜 https://blog.csdn.net/zhy295006359/article/details/77412566 假设感觉需要走大电流,那么就 ...

  3. VC++编写简单串口上位机程序

    VC++编写简单串口上位机程序   转载: http://blog.sina.com.cn/s/articlelist_1809084904_0_1.html VC++编写简单串口上位机程序 串口通信 ...

  4. mysql大数据量下的分页

    mysql大数据量使用limit分页,随着页码的增大,查询效率越低下. 测试实验 1.   直接用limit start, count分页语句, 也是我程序中用的方法: select * from p ...

  5. Socket异步通信及心跳包同时响应逻辑分析(最后附Demo)。

    有段时间没有更博了,刚好最近在做Socket通信的项目,原理大致内容:[二维码-(加logo)]-->提供主机地址和端口号信息(直接使用[ThoughtWorks.QRCode.dll]比较简单 ...

  6. Git push 时如何避免出现 "Merge branch 'master' of ..."

    在使用 Git 的进行代码版本控制的时候,往往会发现在 log 中出现 "Merge branch 'master' of ..." 这句话,如下图所示.日志中记录的一般为开发过程 ...

  7. Gitblit版本服务器环境部署记录

    Gitblit介绍Gitblit 是一个纯 Java 库用来管理.查看和处理 Git 资料库.相当于 Git 的 Java 管理工具,支持linux系统.Git是分布式版本控制系统,它强调速度.数据一 ...

  8. 代码规范与复审2——个人博客作业week

    一.关于编程规范的重要性论证 1.不支持. 1)编程规范有利于自己提高编程效率和编程质量.编码是程序员的职责,一个好的信息技术产品必然有高质量的代码,高质量的代码首先 一点它必须遵守某种编程规范.如果 ...

  9. linux内核分析ELF文件分析实践报告

  10. 20135337——Linux实践二:模块

    一.编译&生成&测试&删除 1.编写模块代码,查看如下 gedit 1.c(编写) cat 1.c(查看) MODULE_AUTHOR("Z") MODUL ...