BZOJ4456/UOJ184 [Zjoi2016]旅行者

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:BZOJ4456

       UOJ184

正解:分治+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
",ans[i]);
	//cout<<endl<<clock()<<endl;
}

int main()
{
    work();
    return 0;
}

  

原文地址:https://www.cnblogs.com/ljh2000-jump/p/6481252.html