zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    HTML常用标记(完整版)
    理论精讲-教育知识与能力7-第四章 中学生学习心理
    前端面试题总结
    for-in 和for-of循环的区别
    Nginx部署多个vue前端项目
    vue项目PC端如何适配不同分辨率屏幕
    基于Vue的项目打包为移动端app
    js中Date对象
    React Router的Route的使用
    js中数组的sort() 方法
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6481252.html
Copyright © 2011-2022 走看看