题意:

就是给定一张n nn个点的图,求源点s ss到每个点的单源最短路。
这张图共有q组边,连边方式有3种:

a→b ,边权为w的单向边;
a→[l,r] ,即a到连续区间[l,r]中的每一个点都有一条边权为w的边。
[l,r]→a,即连续区间[l,r] 中的每一个点都有一条到a 边权为w 的边。
注意数据范围n,q≤10^5.

题解

如果暴力连边(将一对多或者多对一的连边拆开分别连边),那么最差情况下,我们的边数将达到O(nq) 级别,连边就直接超出能承受的范围——更不要说再跑最短路了。因此我们考虑如何使用一些数据结构把这些一对多的情况压缩成少数的一条边,使每一个连边操作都是O(1) O(1)的。
注意到所有“一对多”和“多对一“中“多”都是连续区间,回想我们平时处理连续区间的时候,最经常采用的一个数据结构就是线段树了。于是我们把线段树抓两棵过来放在那边。

那我们怎么用线段树呢?

我们在做区间加法的时候,遇到区间问题,我们都是往下查点,直到以这个点为根节点的子树完全包含于我们需要的区间内的时候,直接返回这个点的值——那么我们也可以采用同样的思想:如果我们把线段树的叶节点当做是原图的点,那么对于以这个点为根节点的子树的所有叶节点如果都在范围内,那么我们是不是可以直接在这个子树的根节点的地方连边呢?这样,对于这样一个子区间,我们只需要1条边。至于我们给出的一段区间会被拆成几个小区间,根据线段树的基本知识,我们知道它是常数级别的,因此我们连边就全部变成了O(1)的。

慢着,我们刚才说连边连在了线段树的中间,可是原图的点是叶节点,

那么我们怎么才能走到中间的那些点呢?

于是我们就只好将每一个点向父节点连边,边权为0。这样就解决问题了?
并没有。因为我们连完边要跑最短路啊,如果我们都向父节点连边,那么中间代表区间的点又应该连向哪里呢?难道连向叶节点吗?这样只能解决多对一的问题,而一对多却不能解决。同样,如果由父节点连向儿子结点,也是不对的。
那么我们就开2 棵线段树吧!我们令一颗叫做“入”树(intree),一棵叫做“出”树(outree),其中intree内部从下往上连0权边,outree内部从上往下连边0权边,那么我们对应的连边操作就是:

对于第一种,我们直接将intree对应u的叶节点连向outree对应v的点。
对于第二种,我们将intree对应a的叶节点连向一个或少数几个outree对应的代表区间的结点。
对于第三种,我们将一个或少数几个intree对应的代表区间的结点连向outree对应a的叶节点。
特别的,因为我们对于每个点在intree和outree都有直接对应的结点,他们实际上表示的是同一个东西,所以我们在intree和outree的叶节点之间对应的连上无向边。
最后,我们只需要跑一遍从intree的对应s结点跑一遍到outree对应所有结点的最短路即可。

时间复杂度:O((n+q)log2n+最短路时间复杂度) 

空间复杂度:O(n) 。

(简单来说就是将对应区间(建图)内的点的权值赋为0,以区间对应的线段树上的结点作为一个点与对应点建图(区间对应的根结点与点建图),然后就是跑一遍最短路就行)

C++代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
const ll MAXN=;
const ll MAXM=;
long long INF; struct node{//segment_tree
ll id;
ll l,r;
}intree[MAXN<<],outree[MAXN<<]; //两种写法均可
//struct edge{//graph::edge
// ll v;
// long long w;
// edge *nxt;
// edge(){
// v=w=0;
// nxt=NULL;
// }
//}*head[MAXM]; struct edge{
ll v;
long long w;
ll nxt;
}e[MAXM<<]; //记得开大,不然re struct road{//dijkstra
ll i;
long long dis;
bool operator<(const road &rhs)const{
return dis>rhs.dis;
}
};
priority_queue<road>q;
ll n,m,s;
ll innum[MAXN],ounum[MAXN];
long long d[MAXM];
ll vis[MAXM],cnt=;
ll tot = ;
ll head[MAXM<<];
void adde(ll u,ll v,long long w){
// edge *p=new edge;
// p->v=v;p->w=w;
// p->nxt=head[u];
// head[u]=p;
tot++;
e[tot].v = v;
e[tot].w = w;
e[tot].nxt = head[u];
head[u] = tot;
} ll inbuild(ll o,ll l,ll r){
intree[o].l=l;intree[o].r=r;intree[o].id=cnt++;
if(l==r)
return innum[l]=intree[o].id;
ll mid=(l+r)>>;
adde(inbuild(lson(o),l,mid),intree[o].id,);
adde(inbuild(rson(o),mid+,r),intree[o].id,);
return intree[o].id;
}
ll oubuild(ll o,ll l,ll r){
outree[o].l=l;outree[o].r=r;outree[o].id=cnt++;
if(l==r)
return ounum[l]=outree[o].id;
ll mid=(l+r)>>;
adde(outree[o].id,oubuild(lson(o),l,mid),);
adde(outree[o].id,oubuild(rson(o),mid+,r),);
return outree[o].id;
}
void inmodify(ll o,ll l,ll r,ll fa,long long w){
if(intree[o].l>=l&&intree[o].r<=r){
adde(intree[o].id,fa,w);
return;
}
ll mid=(intree[o].l+intree[o].r)>>;
if(r<=mid)
inmodify(lson(o),l,r,fa,w);
else if(l>=mid+)
inmodify(rson(o),l,r,fa,w);
else{
inmodify(lson(o),l,mid,fa,w);
inmodify(rson(o),mid+,r,fa,w);
}
}
void oumodify(ll o,ll l,ll r,ll fa,long long w){
if(outree[o].l>=l&&outree[o].r<=r){
adde(fa,outree[o].id,w);
return;
}
ll mid=(outree[o].l+outree[o].r)>>;
if(r<=mid)
oumodify(lson(o),l,r,fa,w);
else if(l>=mid+)
oumodify(rson(o),l,r,fa,w);
else{
oumodify(lson(o),l,mid,fa,w);
oumodify(rson(o),mid+,r,fa,w);
}
} void sssp(){
memset(d,0x7f,sizeof(d));INF=d[];
q.push((road){innum[s],});d[innum[s]]=;
while(!q.empty()){
road t=q.top();q.pop();
if(d[t.i]!=t.dis)
continue;
for(ll i=head[t.i];i!=NULL;i=e[i].nxt){
if(t.dis+e[i].w<d[e[i].v]){
ll v=e[i].v;
d[v]=t.dis+e[i].w;
q.push((road){v,d[v]});
}
}
}
} int main(){
scanf("%lld%lld%lld",&n,&m,&s);
inbuild(,,n);oubuild(,,n);
for(ll i=;i<=n;i++){
adde(innum[i],ounum[i],);
adde(ounum[i],innum[i],);
}
while(m--){
ll opt,v,l,r,w;scanf("%lld",&opt);
switch(opt){
case :{
scanf("%lld%lld%lld",&l,&v,&w);
adde(innum[l],ounum[v],w);
break;
}
case :{
scanf("%lld%lld%lld%lld",&v,&l,&r,&w);
oumodify(,l,r,innum[v],w);
break;
}
case :{
scanf("%lld%lld%lld%lld",&v,&l,&r,&w);
inmodify(,l,r,ounum[v],w);
break;
}
}
}
sssp();
for(ll i=;i<=n;i++)
if(d[ounum[i]]!=INF)
printf("%lld%c",d[ounum[i]]," \n"[i==n]);
else
printf("-1%c"," \n"[i==n]);
return ;
}

786B - Legacy(线段树 + 最短路)线段树优化建图的更多相关文章

  1. BZOJ 4276 [ONTAK2015]Bajtman i Okrągły Robin 费用流+线段树优化建图

    Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢 ...

  2. 【BZOJ4383】[POI2015]Pustynia 线段树优化建图

    [BZOJ4383][POI2015]Pustynia Description 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r ...

  3. AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图

    AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图 链接 AtCoder 大意 在数轴上放上n个点,点i可能的位置有\(x_i\)或者\(y_i\ ...

  4. loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点

    loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点 链接 loj 思路 用交错关系建出图来,发现可以直接缩点,拓扑统计. 完了吗,不,瓶颈在于边数太多了,线段树优化建图. 细节 ...

  5. bzoj3073: [Pa2011]Journeys 线段树优化建图

    bzoj3073: [Pa2011]Journeys 链接 BZOJ 思路 区间和区间连边.如何线段树优化建图. 和单点连区间类似的,我们新建一个点,区间->新点->区间. 又转化成了单点 ...

  6. BZOJ 3073: [Pa2011]Journeys Dijkstra+线段树优化建图

    复习一下线段树优化建图:1.两颗线段树的叶子节点的编号是公用的. 2.每次连边是要建两个虚拟节点 $p1,p2$ 并在 $p1,p2$ 之间连边. #include <bits/stdc++.h ...

  7. bzoj4383 [POI2015]Pustynia 拓扑排序+差分约束+线段树优化建图

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4383 题解 暴力的做法显然是把所有的条件拆分以后暴力建一条有向边表示小于关系. 因为不存在零环 ...

  8. codeforces 787D - Legacy 线段树优化建图,最短路

    题意: 有n个点,q个询问, 每次询问有一种操作. 操作1:u→[l,r](即u到l,l+1,l+2,...,r距离均为w)的距离为w: 操作2:[l,r]→u的距离为w 操作3:u到v的距离为w 最 ...

  9. CF786B Legacy 线段树优化建图

    问题描述 CF786B LG-CF786B 题解 线段树优化建图 线段树的一个区间结点代表 \([l,r]\) 区间点. 然后建立区间点的时候就在线段树上建边,有效减少点的个数,从而提高时空效率. 优 ...

随机推荐

  1. Python GUI教程(六):使用Qt设计师进行窗口布局

    本篇介绍使用qt设计师进行GUI窗口的布局管理,主要包含以下内容: 使用Qt设计师布局我们的窗口部件: 垂直布局: 水平布局: 网格布局: 使用间隔: 使用“伙伴”将label标签与窗口部件进行连接. ...

  2. oracle ROW_NUMBER() OVER(PARTITION BY '分组' ORDER BY '排序' DESC) 用法

    转载:https://blog.csdn.net/dbagaoshou/article/details/51330829 SELECT * FROM ( SELECT ROW_NUMBER() OVE ...

  3. python3笔记八:python数据类型-Number数字

    一:学习内容 数字类型分类:整数.浮点数.复数.布尔值 数字类型转换 数字类型的数学功能:abs().max().min().pow().round().math函数.random函数 二:数字类型分 ...

  4. 布尔 kotlin(4)

    布尔布尔用 Boolean 类型表示,它有两个值: true 和 false .若需要可空引用布尔会被装箱.内置的布尔运算有:||       – 短路逻辑或&&     – 短路逻辑 ...

  5. perf 命令

    perf 是用来进行软件性能分析的工具.通过它,应用程序可以利用 PMU,tracepoint 和内核中的特殊计数器来进行性能统计. 它不但可以分析指定应用程序的性能问题,也可以用来分析内核的性能问题 ...

  6. C# 利用AForge.NET开源类库实现 图像素描效果

    引入DLL: using AForge.Imaging; using AForge.Imaging.Filters; //using AForge.Video.DirectShow;可以使用摄像头图像 ...

  7. 【转】How-to: Enable User Authentication and Authorization in Apache HBase

    With the default Apache HBase configuration, everyone is allowed to read from and write to all table ...

  8. failed to create cublas handle: CUBLAS_STATUS_ALLOC_FAILED 错误解决方法

    解决: config = tf.ConfigProto() config.gpu_options.allow_growth = True session = tf.Session(config=con ...

  9. 找回从pycharm中删除的文件及文件夹

  10. flex布局使用方法简要汇总

    近期因为使用inline-block,在项目部署后出现了一点小问题,几番解决未果,遂使用flex布局,于是借此机会对flex布局的用法做一个简要的汇总. 遇到的bug如下: 本想两个div使用inli ...