[BZOJ4699]树上的最短路(最短路+线段树)
https://www.cnblogs.com/Gloid/p/10273902.html
这篇文章已经从头到尾讲的非常清楚了,几乎没有什么需要补充的内容。
首先$O(n\log^2 n)$的做法比较显然,倍增优化建图+最短路即可。
然后利用“每个塌陷最多会被使用一次”的性质,为每个塌陷(边也看作一种塌陷)建一个点跑一个变体的Dijkstra就可以优化到$O((n+m)\log n)$。
这里讲下我最后一步的实现。
为每个塌陷找未标记的点很简单,并查集f[i]表示离i最近的未被标记的祖先,每次标记i后f[i]=get(fa[i])即可。
为每个点找未标记的塌陷相对复杂,分两种情况考虑。
一是x是这个塌陷的LCA,这个很好处理,开个vector存储以每个点为LCA的所有塌陷即可。
二是塌陷的一个端点在x子树内,一个在子树外。
设x的子树的DFS序区间为[L,R]
考虑每次取出“一个端点在x的子树中,另一个端点的DFS序尽量小”的未标号的塌陷,若此塌陷的另一个端点在L之前则说明这个塌陷过点x。
更新完后再把这个塌陷删掉,直到取出的塌陷在L之后。同理,对“尽量大”的塌陷也做一遍,直到这个塌陷另一个端点DFS序在R之前。
于是我们要支持的是,查询一个端点在某个区间中的所有塌陷的另一个端点最小/大值。
线段树维护每个区间中的这个信息,同时在线段树的叶子上,用堆维护“以这个点为端点的所有塌陷的另一个端点”。
由于尽量小和尽量大都要做一边,所以要开一个大根堆和一个小根堆。
实现起来可能没有什么细节,只是要注意代码不要写残,自己证一遍复杂度,否则可能不小心就写成$O(n\log^2 n)$的了。
#include<queue>
#include<cstdio>
#include<algorithm>
#define ls (x<<1)
#define rs (ls|1)
#define lson ls,L,mid
#define rson rs,mid+1,R
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,M=;
ll inf=1e15;
bool tag[M],b[M];
ll dis[M];
int n,m,S,u,v,w,cnt,tot,tim;
int pos[N],f[N],L[N],R[N],dep[N],fa[N][];
int h[N],to[N<<],nxt[N<<],mn[N<<],mn1[N<<],mx[N<<],mx1[N<<];
struct E{ int L1,R1,L2,R2,w; }e[M];
vector<int>ve[N]; struct P{ int id,s; };
bool operator <(const P &a,const P &b){ return a.s<b.s; }
struct Cmp{ bool operator ()(const P &a,const P &b){ return a.s>b.s; } };
priority_queue<P,vector<P>,Cmp>Q1[N];
priority_queue<P>Q2[N]; struct D{ int x; ll d; };
bool operator <(const D &a,const D &b){ return a.d>b.d; }
priority_queue<D>Q; void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
int get(int x){ return f[x]==x ? x : f[x]=get(f[x]); } void dfs(int x){
dep[x]=dep[fa[x][]]+; L[x]=++tim; pos[tim]=x;
rep(i,,) fa[x][i]=fa[fa[x][i-]][i-];
For(i,x) if ((k=to[i])!=fa[x][]) fa[k][]=x,dfs(k);
R[x]=tim;
} int Lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int t=dep[x]-dep[y];
for (int i=; ~i; i--) if (t&(<<i)) x=fa[x][i];
if (x==y) return x;
for (int i=; ~i; i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][];
} void upd(int x){
if (mn1[ls]<mn1[rs]) mn[x]=mn[ls],mn1[x]=mn1[ls]; else mn[x]=mn[rs],mn1[x]=mn1[rs];
if (mx1[ls]>mx1[rs]) mx[x]=mx[ls],mx1[x]=mx1[ls]; else mx[x]=mx[rs],mx1[x]=mx1[rs];
} void build(int x,int L,int R){
if (L==R){
int p=pos[L]; mn1[x]=n+; mx1[x]=-;
if (!Q1[p].empty()) mn[x]=Q1[p].top().id,mn1[x]=Q1[p].top().s;
if (!Q2[p].empty()) mx[x]=Q2[p].top().id,mx1[x]=Q2[p].top().s;
return;
}
int mid=(L+R)>>; build(lson); build(rson); upd(x);
} void que(int x,int L,int R,int l,int r,bool k,int &s,int &s1){
if (L==l && r==R){
if (!k) s=mn[x],s1=mn1[x]; else s=mx[x],s1=mx1[x];
return;
}
int mid=(L+R)>>;
if (r<=mid) que(lson,l,r,k,s,s1);
else if (l>mid) que(rson,l,r,k,s,s1);
else{
int a,a1,b,b1;
que(lson,l,mid,k,a,a1); que(rson,mid+,r,k,b,b1);
if (!k){ if (a1<b1) s=a,s1=a1; else s=b,s1=b1; }
else { if (a1>b1) s=a,s1=a1; else s=b,s1=b1; }
}
} void del(int x,int L,int R,int p,bool k){
if (L==R){
p=pos[p];
if (!k){
Q1[p].pop(); mn[x]=; mn1[x]=n+;
if (!Q1[p].empty()) mn[x]=Q1[p].top().id,mn1[x]=Q1[p].top().s;
}else{
Q2[p].pop(); mx[x]=; mx1[x]=-;
if (!Q2[p].empty()) mx[x]=Q2[p].top().id,mx1[x]=Q2[p].top().s;
}
return;
}
int mid=(L+R)>>;
if (p<=mid) del(lson,p,k); else del(rson,p,k);
upd(x);
} void solve1(int x){
int u=e[x].L2,v=e[x].R2,lca=Lca(u,v);
u=f[u]; v=f[v];
while (dep[u]>=dep[lca] || dep[v]>=dep[lca]){
if (!u && !v) break;
if (dep[u]<dep[v]) swap(u,v);
if (!tag[u]) dis[u]=min(dis[u],dis[x+n]),Q.push((D){u,dis[u]});
tag[u]=; u=f[u]=get(fa[u][]);
}
} void solve2(int x){
int ed=ve[x].size()-;
rep(i,,ed){
int k=ve[x][i];
if (!tag[k+n]) tag[k+n]=,dis[k+n]=min(dis[k+n],dis[x]+e[k].w),Q.push((D){k+n,dis[k+n]});
}
while (){
int mn,mn1,mx,mx1;
que(,,n,L[x],R[x],,mn,mn1);
que(,,n,L[x],R[x],,mx,mx1);
if (mn1>=L[x]) mn=;
if (mx1<=R[x]) mx=;
if (!mn && !mx) break;
if (mn){
if (!tag[mn+n]) dis[mn+n]=min(dis[mn+n],dis[x]+e[mn].w),Q.push((D){mn+n,dis[mn+n]});
tag[mn+n]=; del(,,n,L[e[mn].L1]+L[e[mn].R1]-mn1,);
}
if (mx){
if (!tag[mx+n]) dis[mx+n]=min(dis[mx+n],dis[x]+e[mx].w),Q.push((D){mx+n,dis[mx+n]});
tag[mx+n]=; del(,,n,L[e[mx].L1]+L[e[mx].R1]-mx1,);
}
}
} void Dij(){
rep(i,,n+tot) dis[i]=inf; dis[S]=; Q.push((D){S,}); tag[S]=;
while (!Q.empty()){
int x=Q.top().x; Q.pop();
if (b[x]) continue;
b[x]=;
if (x>n) solve1(x-n); else solve2(x);
}
} int main(){
freopen("bzoj4699.in","r",stdin);
freopen("bzoj4699.out","w",stdout);
scanf("%d%d%d",&n,&m,&S);
rep(i,,n) scanf("%d%d%d",&u,&v,&w),e[++tot]=(E){u,u,v,v,w},e[++tot]=(E){v,v,u,u,w},add(u,v),add(v,u);
dfs();
rep(i,,n) f[i]=i;
rep(i,,m) tot++,scanf("%d%d%d%d%d",&e[tot].L2,&e[tot].R2,&e[tot].L1,&e[tot].R1,&e[tot].w);
rep(i,,tot){
int u=e[i].L1,v=e[i].R1,lca=Lca(u,v);
ve[lca].push_back(i);
Q1[u].push((P){i,L[v]}); Q2[u].push((P){i,L[v]});
Q1[v].push((P){i,L[u]}); Q2[v].push((P){i,L[u]});
}
build(,,n); Dij();
rep(i,,n) printf("%lld\n",dis[i]);
return ;
}
[BZOJ4699]树上的最短路(最短路+线段树)的更多相关文章
- 51 nod 1766 树上的最远点对(线段树+lca)
1766 树上的最远点对 基准时间限制:3 秒 空间限制:524288 KB 分值: 80 难度:5级算法题 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个 ...
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
- 51nod 1766 树上的最远点对——线段树
n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j& ...
- 树上最长不下降链 线段树合并+set
读错题了,然后写了一个树上 LIS,应该是对的吧...... code: #include <bits/stdc++.h> #define N 200005 #define LL long ...
- Wannafly挑战赛2_D Delete(拓扑序+最短路+线段树)
Wannafly挑战赛2_D Delete Problem : 给定一张n个点,m条边的带权有向无环图,同时给定起点S和终点T,一共有q个询问,每次询问删掉某个点和所有与它相连的边之后S到T的最短路, ...
- 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)
传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...
- [Vani有约会]雨天的尾巴——树上差分+动态开点线段树合并
题目描述 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所 ...
- [XJOI NOI2015模拟题13] C 白黑树 【线段树合并】
题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动 ...
- BZOJ3237:[AHOI2013]连通图(线段树分治,并查集)
Description Input Output Sample Input 4 5 1 2 2 3 3 4 4 1 2 4 3 1 5 2 2 3 2 1 2 Sample Output Connec ...
- BZOJ4785 ZJOI2017树状数组(概率+二维线段树)
可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...
随机推荐
- python3学习笔记.5.打包发布
为了给别人使用将 .py 文件打包成 .exe 文件 安装 PyInstaller ,完成 打开 Cmd 调用 path:\pyinstaller.exe -F path:\name.py 发布文件 ...
- JS设计模式——6.方法的链式调用
什么是链式调用 这个很容易理解,例如: $(this).setStyle('color', 'red').show(); 分解链式调用 链式调用其实是两个部分: 1.操作对象(也就是被操作的DOM元素 ...
- 聊聊Java的final关键字
Java的final关键字在日常工作中经常会用到,比如定义常量的时候.如果是C++程序员出身的话,可能会类比C++语言中的define或者const关键字,但其实它们在语义上差距还是挺大的. 在Jav ...
- 20165227 学习基础和C语言基础调查
学习基础和C语言基础调查 技能学习经验和感悟 你有什么技能比大多人(超过90%以上)更好? 如果非要说出来一个的话,那就是篮球了.从热爱篮球,到热爱打篮球,经历挫折阻碍,不断反思学习,一步一步地向前迈 ...
- shell用户管理->
用户的添加与删除练习 -> 脚本1(if then) 思路:1.条件测试, 脚本使用案例, 创建用户[交互式创建] 1.怎么交互式 read -p 2.接收到对应字符串怎么创建用户 userad ...
- NLP里面好的学习资料
别人推荐的网址: http://ruder.io/deep-learning-nlp-best-practices/index.html#wordembeddings
- Java基础83 JSP标签及jsp自定义标签(网页知识)
1.JSP标签 替代jsp脚本,用于jsp中执行java代码1.1.内置标签: <jsp:forward></jsp:forward> 相当于:request.getReu ...
- 洛谷P3396哈希冲突
传送门啦 非常神奇的分块大法. 这个题一看数据范围,觉得不小,但是如果我们以 $ \sqrt(x) $ 为界限,数据范围就降到了 $ x < 400 $ 我们设数组 $ f[i][j] $ 表示 ...
- C++两个类相互包含引用的问题
在构造自己的类时,有可能会碰到两个类之间的相互引用问题,例如:定义了类A类B,A中使用了B定义的类型,B中也使用了A定义的类型 class A { B b; } class B { A* a; } 请 ...
- SQL SERVER2008 镜像全攻略
--在非域控环境中创建数据库镜像, 我们必须使用证书来创建数据库镜像. 大致的步骤包括: --在为数据库镜像配置的每个服务器实例上执行下列步骤: --在 master 数据库中,创建数据库主密钥. - ...