这不裸的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的更多相关文章

  1. bzoj3073Journeys(线段树优化最短路)

    这里还是一道涉及到区间连边的问题. 如果暴力去做,那么就会爆炸 那么这时候就需要线段树来优化了. 因为是双向边 所以需要两颗线段树来分别对应入边和出边 QwQ然后做就好了咯 不过需要注意的是,这个边数 ...

随机推荐

  1. c# winform 窗体间的传值

    1.父窗体传值给子窗体: 1) 父窗体: FrmXX frm = ,); frm.Owner = this; frm.ShowDialog(); 子窗体: ; public FrmXX(int ty, ...

  2. gperftools cpp wrapper

    gperftools cpp wrapper // Compile command : ${CXX} -o test_profiler.elf -DUSE_GPERFTOOLS -DDEBUG -D_ ...

  3. LeetCode 腾讯精选50题--有效的括号

    根据题意,第一反应就是使用栈,左右括号相匹配,则将左括号出栈,否则将左括号入栈. 这里我用数组配合“指针”模拟栈的入栈与出栈操作,初始时指针位置指向0,表示空栈,凡遇上左括号则直接入栈,若遇上有括号, ...

  4. 从零开始搭建react应用

    用create-react-app搭建react应用,了解npm run start的工作过程. 第一步:安装脚手架 create-react-app 1. 在node里 npm install cr ...

  5. php操作 cookie

    1,设置cookie <?php setcookie('key'); setcookie('key1','value1'); setcookie(***); setcookie('key4', ...

  6. 《Redis高阶应用》讲座总结

    数据结构延展 常用数据结构:String,Hash,List,Set,Sorted Set(不聊这些) 高级数据结构:Bitmaps,hyperloglog,GEO 单机拓展到分布式 为什么要分区:性 ...

  7. jeesite表字段太多导致不能自动生成那张表的代码——————jetty 之 form too large | form too many keys 异常

    看了Jetty的源码才发现,jetty限制了Form提交数据的大小,该源码类来自jetty lib库下的jetty-server-7.6.16.v20140903.jar包下的 org.eclipse ...

  8. TCP中异常关闭的情况记录

    1.当TCP连接的对端进程已经关闭了Socket的情况下,本端进程再发送数据时,第一包可以发送成功(但会导致对端发送一个RST包过来):之后如果再继续发送数据会失败,错误码为“10053: An es ...

  9. Delphi 图形组件(Shape)

    樊伟胜

  10. CPU排行

    早期的时候,在某个网站上面收录下来的关于CPU排行的信息,发出来让更多的人看到,希望能够帮助到一些人!