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然后做就好了咯 不过需要注意的是,这个边数 ...
随机推荐
- warning LNK4076: 无效的增量状态文件“../×××.ilk”;正在非增量链接
VS编译警告:warning LNK4076: 无效的增量状态文件“../×××.ilk”;正在非增量链接 解决方法:删除程序提示的输出目录的×××.ilk,重新编译,即可
- O036、Snapshot Instance 操作详解
参考https://www.cnblogs.com/CloudMan6/p/5510296.html 有时候系统损坏的很严重,通过 Rescue 操作无法修复,那么我们就得重新考虑通过备份恢复了. ...
- Access to XMLHttpRequest at 'http://localhost:8090/user/getotp' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
ajax跨域请求错误 解决: springboot中: 加注解 @CrossOrigin
- javascript框架(库)
javascript框架(库)高级JavaScript编程,尤其是复杂的浏览器差异处理,通常是困难和耗时的.为了响应这些调整,出现了许多javascript(helper)库.这些JavaScript ...
- OSCP-Kioptrix2014-3 后渗透测试
拿到root权限 之前的努力,最终获得了两个session 尝试看看该操作系统的漏洞 kali: searchsploit freebsd 9.0 cp /usr/share/exploitdb/ex ...
- 基于Java使用Flink读取CSV文件,针对批处理,多表联合两种方式Table类和Join方法的实现数据处理,再入CSV文件
Maven依赖 源头 <dependencies> <dependency> <groupId>org.projectlombok</groupId> ...
- Hive的日志操作
想要看hive的日志,我们查看/home/hadoop/hive/conf/hive-log4j2.properties # list of properties property.hive.log. ...
- Delphi DeviceIoControl函数
- ClassLoader心得
我们都知道,jvm执行的代码,都是通过jvm加载系统加入的.加载系统的第一步是通过ClassLoader加载class二进制信息,jvm规范中并没有规定class的来源类型,这就给 ...
- Ruby2.0后版本的debug工具: byebug
https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md 安装: gem install byebug 使用: Rails: 直接 ...