Bzoj3073Journeys
这不裸的dij吗?来,弄他。
打完以后发现不妙,这数据范围略神奇……算一算,考一场都可能跑不出来。map去重边(成功额外引入log)不怕,交。TLE,54。
这不玩呢吗,把map去了,交。MLE,71……这题考场性价比可能挺高的。
尝试改成vector,没省内存,反而更慢了。
发现以前没学过的一个神奇知识点——线段树优化建图。
这东西,一般出现某个区间和另一个区间内的若干元素建边时使用,因为一个点一个点的建肯定Boom。
这时,根据我们对线段树的理解,我们可以把线段树上的节点当成图中的节点,跟据线段树上一个节点可以表示一个区间这个性质就可以加快建边了,剩下的就是跑一个dij的事。
具体一点,过程如下(你看完这段话肯能还得看看别的博客):
1.建立一颗线段树,叫做“出树”,代表离开一个节点的信息,出树的所有儿子建边指向父亲,边权为零,举个栗子,可以理解为,从1出去,也是从[1,2]中出去,也是从[1,4]中出去,同时没有额外的花费。
2.建立另一颗线段树,叫做“入树”,代表进入一个节点的信息,入树的所有父亲建边指向儿子,边权为零,举个核桃,可以理解为,进入1,也是进入[1,2],也是进入[1,4],同时没有花费。(当然你也可以不这么理解,因为容易混)。
3.在两颗线段树之间建边,入树的底层1指向出树的底层1,入树的底层2指向出树的底层2。可以理解为,进入这个节点后就可以离开这个节点,边权依旧为零。
4.修改,额外开一个新的节点作为中转站(博主试过开两个中间节点的打法,感觉有点奇怪,没试过不开中间节点的,感兴趣的读者可以试一试),把我们要修改的区间分成约log个小区间,和求区间和有点像,就是把[1,4]拆成[1,3]和4这种(自己去画画线段树吧),然后让出树的若干区间指向这个节点,同时带上边权,这道题就是1喽。然后让这个节点再次指向入树的若干区间,权值为零(这块可能也得好好理解一下),别忘了这可是双向边,倒过来在连一遍,就是重复操作4一次,记得新开点,权值给对(这么看来这个中转站好像就只是让代码好打了一些,没有什么实际意义,因为你直接连这两个节点也可以,但是大家可以自己脑补一下代码,可能略恐怖,当然,你是神犇你任性)。
剩下的就是dijkstra了,没学的自己去学,想看板子的给个小链接:最短路
自行理解吧,博主也得去在画画,思考思考。
代码里有个pos数组,是指原题某个数对应的线段树节点,也就是双向存储的另一方。
复杂度的话,网上有各种各样的版本,博主给个大概,边数最坏大约是O(6n+2mlogn),点数大约是O(6n+m),那么根据dijkstra的复杂度为O((n+m)logn)(一般n的规模不大于m时,近似成O(mlogn)) 这样把n代入边数,m代入点数,比原来的O(n*m^2)强了好多。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
struct EDGE{
int ed,nex,w;
}edge[];int first[],num;
struct node{
int id,num;
friend bool operator < (const node &a,const node &b){
return a.num>b.num;
}
}now;
int dis[];bool v[];
struct Sengment_Tree{
int ls,rs,l,r;
}tr[],tc[];//入树,出树。
int pos[],p1,p2,tot,root1,root2;
int n,m,P;
int read(){
int sum=,f=;char x=getchar();
while(x<''||x>''){
if(x=='-') f=-;
x=getchar();
}while(x>=''&&x<=''){
sum=sum*+x-'';
x=getchar();
}return sum*f;
}
void add(int st,int ed,int w){
// cout<<" st="<<st<<" ed="<<ed<<" w="<<w<<endl;
edge[++num].ed=ed;
edge[num].w=w;
edge[num].nex=first[st];
first[st]=num;
}
void buildtc(int &p,int l,int r){
p=++tot;
tc[p].l=l;tc[p].r=r;
if(l==r) return ;
int mid=l+r>>;
buildtc(tc[p].ls,l,mid);
buildtc(tc[p].rs,mid+,r);
add(tc[p].ls,p,);
add(tc[p].rs,p,);
}
void buildtr(int &p,int l,int r){
p=++tot;
tr[p].l=l;tr[p].r=r;
if(l==r){
pos[l]=p;
return ;
}
int mid=l+r>>; buildtr(tr[p].ls,l,mid);
buildtr(tr[p].rs,mid+,r);
add(p,tr[p].ls,);
add(p,tr[p].rs,);
}
void buildmid(int p){
if(tc[p].l==tc[p].r){
add(pos[tc[p].l],p,);
return ;
}
buildmid(tc[p].ls);
buildmid(tc[p].rs);
}
void changetc(int p,int l,int r){ if(l<=tc[p].l&&tc[p].r<=r){
add(p,p1,);
return;
}
int mid=tc[p].l+tc[p].r>>;
if(l<=mid) changetc(tc[p].ls,l,r);
if(r>mid) changetc(tc[p].rs,l,r);
}
void changetr(int p,int l,int r){
if(l<=tr[p].l&&tr[p].r<=r){
add(p1,p,);
return ;
}
int mid=tr[p].l+tr[p].r>>;
if(l<=mid) changetr(tr[p].ls,l,r);
if(r>mid) changetr(tr[p].rs,l,r);
}
void dijkstra(int st){
memset(dis,0x7f,sizeof(dis));
priority_queue<node>q;
dis[pos[st]]=;
now.id=pos[st];now.num=;
q.push(now);
while(!q.empty()){
now=q.top();q.pop();
int x=now.id;
if(v[x]) continue;
v[x]=;
for(int i=first[x];i;i=edge[i].nex){
int y=edge[i].ed;
if(dis[y]>dis[x]+edge[i].w){
dis[y]=dis[x]+edge[i].w;
now.id=y;now.num=dis[y];
q.push(now);
}
}
}
}
void change(int a,int b,int c,int d){
p1=++tot;
changetc(root1,a,b);
changetr(root2,c,d);
}
int main(){
/*freopen("11.in","r",stdin);
freopen("11.out","w",stdout);*/
n=read();m=read();P=read();
buildtc(root1,,n);buildtr(root2,,n);buildmid(root1);
for(int i=,a,b,c,d;i<=m;i++){
a=read();b=read();c=read();d=read();
change(a,b,c,d);
change(c,d,a,b);
}
dijkstra(P);
for(int i=;i<=n;i++)
printf("%d\n",dis[pos[i]]);
return ;
}
出树子指父,
入树父指儿。
横叉入指出,
新边指两边。
Bzoj3073Journeys的更多相关文章
- bzoj3073Journeys(线段树优化最短路)
这里还是一道涉及到区间连边的问题. 如果暴力去做,那么就会爆炸 那么这时候就需要线段树来优化了. 因为是双向边 所以需要两颗线段树来分别对应入边和出边 QwQ然后做就好了咯 不过需要注意的是,这个边数 ...
随机推荐
- Django-DRF-视图的演变(二)
Django-DRF-视图的演变 版本一(基于类视图APIView类) views.py: APIView是继承的Django View视图的. 1 from .serializers impor ...
- Java并发编程——线程池
本文的目录大纲: 一.Java中的ThreadPoolExecutor类 二.深入剖析线程池实现原理 三.使用示例 四.如何合理配置线程池的大小 一.Java中的ThreadPoolExecutor类 ...
- Extjs中,Vo对象中的属性无法在data中获取的解决方法
store.getById(data.data.id).raw.redpackid
- js之数据类型(对象类型——构造器对象——数组1)
数组是值的有序集合,每个值叫做一个元素,而每一个元素在数组中有一个位置,以数字表示,称为索引.JavaScript数组是无类型的,数组元素可以是任意类型且同一个数组中不同元素也可能有不同的类型.数组的 ...
- element-ui 表格可编辑添加删除
<template> <div id="Cold_all"> <div class="Cold_Left"> <el- ...
- head pose estimation
opencv:帖子中介绍了算法原理和opencv估计姿态的代码 https://www.learnopencv.com/head-pose-estimation-using-opencv-and-dl ...
- 5、vim编辑器
1.什么是VIM? 理解为windows下面的文本编辑器,比如记事本,比如word文档 2.为什么要学? 因为在后面我们配置的服务,都需要人为修改配置,以便让程序按照我们修改后的指示运行. 1.修改配 ...
- 第十一章·Filebeat-使用Filebeat收集日志
Filebeat介绍及部署 Filebeat介绍 Filebeat附带预构建的模块,这些模块包含收集.解析.充实和可视化各种日志文件格式数据所需的配置,每个Filebeat模块由一个或多个文件集组成, ...
- jdk提供的线程协调API suspend/resume wait/notify park/unpark
线程通信(如 线程执行先后顺序,获取某个线程执行的结果等)有多种方式: 文件共享 线程1 --写入--> 文件 < --读取-- 线程2 网络共享 变量共享 线程1 --写入--> ...
- PAT Basic 1092 最好吃的月饼 (20 分)
月饼是久负盛名的中国传统糕点之一,自唐朝以来,已经发展出几百品种. 若想评比出一种“最好吃”的月饼,那势必在吃货界引发一场腥风血雨…… 在这里我们用数字说话,给出全国各地各种月饼的销量,要求你从中找出 ...