786B - Legacy(线段树 + 最短路)线段树优化建图
题意:
就是给定一张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(线段树 + 最短路)线段树优化建图的更多相关文章
- 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时间中选出一个时间进行抢劫,并计划抢 ...
- 【BZOJ4383】[POI2015]Pustynia 线段树优化建图
[BZOJ4383][POI2015]Pustynia Description 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r ...
- AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图
AtCoder Regular Contest 069 F Flags 二分,2-sat,线段树优化建图 链接 AtCoder 大意 在数轴上放上n个点,点i可能的位置有\(x_i\)或者\(y_i\ ...
- loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点
loj#2255. 「SNOI2017」炸弹 线段树优化建图,拓扑,缩点 链接 loj 思路 用交错关系建出图来,发现可以直接缩点,拓扑统计. 完了吗,不,瓶颈在于边数太多了,线段树优化建图. 细节 ...
- bzoj3073: [Pa2011]Journeys 线段树优化建图
bzoj3073: [Pa2011]Journeys 链接 BZOJ 思路 区间和区间连边.如何线段树优化建图. 和单点连区间类似的,我们新建一个点,区间->新点->区间. 又转化成了单点 ...
- BZOJ 3073: [Pa2011]Journeys Dijkstra+线段树优化建图
复习一下线段树优化建图:1.两颗线段树的叶子节点的编号是公用的. 2.每次连边是要建两个虚拟节点 $p1,p2$ 并在 $p1,p2$ 之间连边. #include <bits/stdc++.h ...
- bzoj4383 [POI2015]Pustynia 拓扑排序+差分约束+线段树优化建图
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4383 题解 暴力的做法显然是把所有的条件拆分以后暴力建一条有向边表示小于关系. 因为不存在零环 ...
- 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 最 ...
- CF786B Legacy 线段树优化建图
问题描述 CF786B LG-CF786B 题解 线段树优化建图 线段树的一个区间结点代表 \([l,r]\) 区间点. 然后建立区间点的时候就在线段树上建边,有效减少点的个数,从而提高时空效率. 优 ...
随机推荐
- [VIJOS2055][SDOI2019]移动金币:DP+组合数学
分析 显然可以转化为阶梯nim. 于是问题转化为了对于所有\(i \in [0,n-m]\),求长度为\(\lfloor\frac{m+1}{2}\rfloor\),和为\(i\),异或和非\(0\) ...
- 使用Hive-JDBC遇到的一些问题解决
使用Hive-JDBC遇到的一些问题解决(转) 标签(空格分隔): Hive 1,java.lang.NoClassDefFoundError: org/apache/hive/service/cli ...
- 为什么要使用 Go 语言,Go 语言的优势在哪里?
1.Go有什么优势 可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了. 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的 ...
- conda install -c anaconda
有些包在conda默认的channels中不包含,比如cudatoolkit-8.0,cudnn等,这时只需要在conda install指令后加上-c anaconda即可.比如要下载cudatoo ...
- SpringMvc 支持一下类型Serlvet 原生的 API 作为目标方法的参数
/** * 可以使用 Serlvet 原生的 API 作为目标方法的参数 具体支持以下类型 * * HttpServletRequest * HttpServletResponse * HttpSes ...
- linux挂载问题
说明 Linux系统在使用光盘.软盘或U盘时,必须先执行挂载(mount)命令. 挂载命令会将这些存储介质指定成系统中的某个目录,以后直接访问相应目录即可读写存储介质上的数据. 挂载光盘 mount ...
- pip Fatal error in launcher: Unable to create process using '""'
如果你装了python2.7, python3.5, 在两个版本的兼容问题上折腾很久了, 通过修改环境变量, 能够出现下面的界面, 恭喜你, 暂时解决了一些问题, 哈哈
- 诺依/RuoYi开源系统搭建总结
问题一:从{码云}下载下来看,输入项目编码不过 解决方法: 加入下列依赖,版本要和下载下来的{spring-boot-dependencies}一致.不一致就会报问题2: <parent> ...
- template要加s,重启服务,不然报错
- 运行上次失败用例(--lf 和 --ff)
前言 “80%的bug集中在20%的模块,越是容易出现bug的模块,bug是越改越多“平常我们做手工测试的时候,比如用100个用例需要执行,其中10个用例失败了,当开发修复完bug后,我们一般是重点测 ...