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然后做就好了咯 不过需要注意的是,这个边数 ...
随机推荐
- 客户端注册Cannot execute request on any known server解决
在对eureka注册中心服务端添加安全验证后,新版本springcloud出现一个问题就是,在客户端注册到服务中心时报了一个错:Cannot execute request on any known ...
- React中使用遍历
1.使用for(let item of items){} render(){ var itemList = [] for(let item of items){ itemList.push(<I ...
- VUE神速搭建项目
1.npm install -g vue-cli 全局安装vue-cli脚手架 2.vue init webpack vueTest 初始化一个基于webpack的项目 3.cd vueTest 进入 ...
- 4 java 笔记
1 javadoc工具默认只处理以public,protected修饰的类,接口,方法,成员变量,构造器和内部类之前的文档注释 2 文档注释以斜线后紧跟两个星号开始/**,以星号后紧跟一个斜线结束*/ ...
- axios跨域问题(包括开发环境和生产环境)
之前写过一篇axios跨域问题,写的过于片面,没有考虑过实际开发中遇到的问题,以及如何全局使用axios,这次再写一篇,以后再有新发现再更新... 1.在static文件夹下新建/js/config. ...
- 第四篇.python的基础
目录 第四篇.python基础01 1. 变量 2. 常量 3. python变量内存管理 4. 变量的三个特征 5. 花式赋值 6. 注释 7. 数据类型基础 8. 数字类型 9. 字符串类型 10 ...
- fastadmin Excel导出时数字被科学计数
/public/assets/libs/bootstrap-table/dist/extensions/export/bootstrap-table-export.min.js //exportOpt ...
- 采用kubeadm部署工具,部署kubernetes1.16.3
安装kubenetes有5种部署工具,分别是kubeadm.kops.KRIB.Kubespray.本实验采用的是kubeadm部署工具.如有想了解其他部署工具,请点击这里 环境说明 角色/主机名 系 ...
- Linux awk命令 --三剑客老大
Linux awk命令 --三剑客老大 基本用法: awk [参数] ['找谁{干啥}'] 文件 参数: -F 分隔符 -v 创建或修改awk变量 OFS 输出分割符 awk显示每一列的时候分隔 ...
- 2.LVS的三种工作模式_NAT模式
1.LVS的三种工作模式 1)VS/NAT模式(Network address translation) 2)VS/TUN模式(tunneling) 3)DR模式(Direct routing) 1. ...