BZOJ4456/UOJ184 [Zjoi2016]旅行者
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
题目链接:BZOJ4456
正解:分治+dijkstra
解题报告:
这道题的思想挺神的…
考虑我如果每次都处理的是一个矩阵中的询问,我选取“长”,将其分割为两半,显然中轴线的长度$<=$ $\sqrt{n*m}$。
我对于中轴线上的所有点,都以其为源点,跑一遍$dijkstra$,然后对位于这个矩阵的所有询问都可以用到左上角和右下角的距离和来$update$一下答案。因为一个询问必然跨越中轴线或者完全处在一边,跨越的询问则一定可以找到答案了,而完全处在一边的则也可能从中轴线上经过,所以也可以更新答案。
之后我对于完全处在一边的,递归下去,分治地做就可以了。
这个复杂度还是有一点虚的,需要优秀的常数技巧,当然,手写堆就丝毫不虚了...
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
using namespace std;
typedef long long LL;
const int MAXN = 50011;
const int MAXM = 400011;
const int inf = (1<<30)-1;
int n,m,Q,ecnt,first[MAXN],to[MAXM],next[MAXM],w[MAXM];
int ans[100011],dis[MAXN],top,id[MAXN],d[MAXM],belong[MAXN][2];
struct ask{ int lx,ly,rx,ry,id; }q[100011],tmp[100011];
inline int get(int x,int y){ return (x-1)*m+y; }
inline void upd(int &x,int y){ if(x>y) x=y; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline void link(int x,int y,int z){
next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
} inline void push(int x){
if(!id[x]) id[x]=++top,d[top]=x;
int u,fa; u=id[x]; fa=u>>1;
while(fa>0) {
if(dis[x]<dis[d[fa]]) {
id[d[fa]]=u;
id[x]=fa;
swap(d[fa],d[u]);//!!!
}
else break;
u=fa; fa=u>>1;
}
} inline void pop(){
id[d[1]]=0; d[1]=d[top--]; if(top>0) id[d[1]]=1;/*!!!*/
int u,lc,rc,pos; u=1; lc=2; rc=3;
while(lc<=top) {
if(dis[d[lc]]>dis[d[rc]]/*!!!*/ && rc<=top) pos=rc;
else pos=lc;
if(dis[d[pos]]>=dis[d[u]]) break;
id[d[pos]]=u;
id[d[u]]=pos;
swap(d[u],d[pos]);
u=pos; lc=u<<1; rc=lc|1;
}
} inline void dijkstra(int lx,int rx,int ly,int ry,int inix,int iniy){
for(int i=lx;i<=rx;i++)
for(int j=ly;j<=ry;j++)
dis[get(i,j)]=inf; int u,nowx,nowy; dis[get(inix,iniy)]=0; push(get(inix,iniy));
while(top>0) {
u=d[1]; pop();
for(int i=first[u];i;i=next[i]) {
int v=to[i]; nowx=belong[v][0]; nowy=belong[v][1];
if(nowx<lx || nowx>rx || nowy<ly || nowy>ry) continue;
if(dis[v]>dis[u]+w[i]) {
dis[v]=dis[u]+w[i];
push(v);
}
}
}
} inline void solve(int lx,int rx,int ly,int ry,int ql,int qr){
if(ql>qr || lx>rx || ly>ry) return ;
if(ql==qr) {//常数优化...
dijkstra(lx,rx,ly,ry,q[ql].lx,q[ql].ly);
upd(ans[q[ql].id],dis[ get(q[ql].lx,q[ql].ly) ] + dis[ get(q[ql].rx,q[ql].ry) ]);
return ;
} int mid,nowl=ql-1,nowr=qr+1;
if(rx-lx>=ry-ly) {//以x坐标为中轴划分
mid=(lx+rx)>>1;
for(int i=ly;i<=ry;i++) {//枚举中轴线上的点
dijkstra(lx,rx,ly,ry,mid,i);
for(int j=ql;j<=qr;j++)
upd(ans[q[j].id],dis[ get(q[j].lx,q[j].ly) ] + dis[ get(q[j].rx,q[j].ry) ]);
} //把询问的两个点都在一测的往下递归,分治处理
for(int i=ql;i<=qr;i++) {
if(q[i].lx<mid && q[i].rx<mid)
tmp[++nowl]=q[i];
else if(q[i].lx>mid && q[i].rx>mid)
tmp[--nowr]=q[i];
}
for(int i=ql;i<=nowl;i++) q[i]=tmp[i];
for(int i=nowr;i<=qr;i++) q[i]=tmp[i];
solve(lx,mid-1,ly,ry,ql,nowl);
solve(mid+1,rx,ly,ry,nowr,qr);
}
else {//切分y坐标
mid=(ly+ry)>>1;
for(int i=lx;i<=rx;i++) {
dijkstra(lx,rx,ly,ry,i,mid);
for(int j=ql;j<=qr;j++)
upd(ans[q[j].id],dis[ get(q[j].lx,q[j].ly) ] + dis[ get(q[j].rx,q[j].ry) ]);
} for(int i=ql;i<=qr;i++) {
if(q[i].ly<mid && q[i].ry<mid)
tmp[++nowl]=q[i];
else if(q[i].ly>mid && q[i].ry>mid)
tmp[--nowr]=q[i];
}
for(int i=ql;i<=nowl;i++) q[i]=tmp[i];
for(int i=nowr;i<=qr;i++) q[i]=tmp[i];
solve(lx,rx,ly,mid-1,ql,nowl);
solve(lx,rx,mid+1,ry,nowr,qr);
}
} inline void work(){
n=getint(); m=getint(); int x,now;
for(int i=n*m;i>=1;i--) { belong[i][0]=(i-1)/m+1; belong[i][1]=(i-1)%m+1; }
for(int i=1;i<=n;i++)
for(int j=1;j<m;j++) {
x=getint(); now=get(i,j);
link(now,now+1,x);
} for(int i=1;i<n;i++)
for(int j=1;j<=m;j++) {
x=getint(); now=get(i,j);
link(now,now+m,x);
} Q=getint(); for(int i=1;i<=Q;i++) ans[i]=inf;
for(int i=1;i<=Q;i++) { q[i].lx=getint(); q[i].ly=getint(); q[i].rx=getint(); q[i].ry=getint(); q[i].id=i; } solve(1,n,1,m,1,Q); for(int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
//cout<<endl<<clock()<<endl;
} int main()
{
work();
return 0;
}
BZOJ4456/UOJ184 [Zjoi2016]旅行者的更多相关文章
- 【BZOJ4456】[Zjoi2016]旅行者 分治+最短路
[BZOJ4456][Zjoi2016]旅行者 Description 小Y来到了一个新的城市旅行.她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北的道路,这些道路两两相交形 ...
- 【BZOJ4456】 [Zjoi2016]旅行者 / 【UOJ #184】 【ZJOI2016】旅行者
Description 小Y来到了一个新的城市旅行.她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北 的道路,这些道路两两相交形成n×m个路口 (i,j)(1≤i≤n,1≤j ...
- bzoj4456: [Zjoi2016]旅行者
题目链接 bzoj4456: [Zjoi2016]旅行者 题解 网格图,对于图分治,每次从中间切垂直于长的那一边, 对于切边上的点做最短路,合并在图两边的答案. 有点卡常 代码 #include< ...
- [BZOJ4456] [Zjoi2016]旅行者 分治+最短路
4456: [Zjoi2016]旅行者 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 777 Solved: 439[Submit][Status] ...
- 4456: [Zjoi2016]旅行者
4456: [Zjoi2016]旅行者 https://www.lydsy.com/JudgeOnline/problem.php?id=4456 分析: 每次对当前矩阵按长边化一条分治线,然后在对分 ...
- P3350 [ZJOI2016]旅行者
题目描述 小Y来到了一个新的城市旅行.她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北的道路,这些道路两两相交形成n*m个路口 (i,j)(1<=i<=n,1&l ...
- luogu3350 [ZJOI2016]旅行者
链接 P3350 [ZJOI2016]旅行者 题目大意:给出网格图,求两点之间最短路,多组询问. \(n*m\leq10^5\ \ q\leq 10^5\) 考虑\(CDQ\)分治. 首先把询问离线, ...
- BZOJ4456/UOJ#184[Zjoi2016]旅行者 分治 最短路
原文链接http://www.cnblogs.com/zhouzhendong/p/8682133.html 题目传送门 - BZOJ4456 题目传送门 - UOJ#184 题意 $n\times ...
- BZOJ4456 ZJOI2016旅行者(分治+最短路)
感觉比较套路,每次在长边中轴线处切一刀,求出切割线上的点对矩形内所有点的单源最短路径,以此更新每个询问,递归处理更小的矩形.因为若起点终点跨过中轴线是肯定要经过的,而不跨过中轴线的则可以选择是否经过中 ...
随机推荐
- freemarker的${!}
${sss!} <#--没有定义这个变量,默认值是空字符串! --> ...................................... 转自:https://blog.csdn ...
- ActiveMQ+SpringMVC+Maven应用示例
本文主要想通过一个WEB示例展示ActiveMQ在具体应用程序中是如何使用的,顺便简单介绍以下如何通过Eclipse搭建一个SpringMVC框架和Maven项目开发框架.下面首先展示一下项目最终的运 ...
- 160711、Java 多线程核心技术梳理
本文对多线程基础知识进行梳理,主要包括多线程的基本使用,对象及变量的并发访问,线程间通信,lock 的使用,定时器,单例模式,以及线程状态与线程组. java 多线程 基础知识 创建线程的两种方式:1 ...
- Drupal 8 提供REST服务实例
drupal8 的核心模块已经支持REST服务. 这样的话使用drupal 对外提供web service 变的简单了. 测试一下d8 的webservice : extend 中的 依赖模块:全部启 ...
- 解决:function in namespace ‘std’ does not name a type + allocator_/nullptr/dellocator_ was not declared + base operand of ‘->’ has non-pointer type ‘std::vector<cv::Mat>’ 错误编译时报错(caffe)
解决方法,用到了c++11,g++命令需要加上-std=c++11选项 附:g++默认的c++标准 gcc-6.4.0 gcc-7.2.0 默认是 -std=gnu++14gcc-4.3.6 gcc- ...
- 使用 Python 编写 vim 插件
使用 Python 编写 vim 插件 - 技术翻译 - 开源中国社区 code {margin: 0;padding: 0;white-space: pre;border: none;backgro ...
- 002-es6字符串扩展
1.字符串扩展 参考地址:http://es6.ruanyifeng.com/#docs/string 1.1.codePointAt() JavaScript 内部,字符以 UTF-16 的格式储存 ...
- java 自制Tomcat Andorid IOS 端 证书
java 自制证书 最近做项目用到Https 需要自制各种证书,Tomcat 用的JKS 格式, Andorid 端使用 BKS 格式, IOS 端使用 P12格式正式, 以及各种证书格式之间的转换. ...
- tarball源码安装
软件最原始的安装方法 用tarball来安装升级make命令执行make ,会在当前路径下搜索makefile这个文本文件,这个文件中记录了源码如何编译的详细信息.软件开发商通常会写一个检测程序,检测 ...
- python中math常用函数
python中math的使用 import math #先导入math包 1 三角函数 print math.pi #打印pi的值 3.14159265359 print math.radians(1 ...