zoukankan      html  css  js  c++  java
  • [ZJOI2016]旅行者-分治+最短路

    一道牛逼题

    题目大意

    给定一张(n*m)的网格图((n*mle 2 imes10^4)),(Q(Qle2 imes10^5))次查询,询问两点之间的距离。

    传送门1

    传送门2

    传送门3

    传送门4

    题目解法

    欢迎来我博客pmt2018查看

    有老哥说看到网格图就想到分治,反正我是听了题解才会的。

    做法是对每次对网格图的长边一切为二,考虑两种情况

    1. 两点不跨过中线,这时两点的最短路有两种情况,要么不跨过中线,我们这时枚举中线上的点用最短路将其更新,要么跨过中线,这时我们将其分治下去继续做。

    2. 两点跨过中线,这时分治下去也没啥用了。

    所以我们把所有询问离线下来,像整体二分一样的处理就好了。

    正确性显然,听说复杂度是(O(Ssqrt S log_2S)​)的,但是我不会证明。

    代码如下

    #include<bits/stdc++.h>
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define se second
    
    #define y0 pmt
    #define y1 pmtpmt
    #define x0 pmtQAQ
    #define x1 pmtQwQ
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef vector<int > vi;
    typedef pair<int ,int > pii;
    const int INF=0x3f3f3f3f, maxn=100007, MOD=1e9+7;
    const ll LINF=0x3f3f3f3f3f3f3f3fLL;
    const ll P=19260817;
    int n,m;
    int Q;
    struct Point{
    	int x,y;
    	Point(){}
    	Point(int _x,int _y){x=_x,y=_y;}
    	friend bool operator < (const Point &x,const Point &y){
    		if(x.x!=y.x)return x.x<y.x;
    		return x.y<y.y;
    	}
    };
    int id(Point x){return (x.x-1)*m+x.y;}
    bool in(Point x,int lx,int rx,int ly,int ry){
    	return lx<=x.x&&x.x<=rx&&ly<=x.y&&x.y<=ry;
    }
    struct edge{
    	int nxt,w;
    	Point to;
    }e[maxn<<2];
    int head[maxn],tot;
    void addedge(Point u,Point v,int w){
    	e[++tot].to=v;
    	e[tot].w=w;
    	e[tot].nxt=head[id(u)];
    	head[id(u)]=tot;
    }
    struct query{
    	Point x,y;
    	int id;
    }q[maxn<<2],q1[maxn<<1],q2[maxn<<1];
    int ans[maxn];
    priority_queue<pair<int ,Point >,vector<pair<int ,Point > >,greater<pair<int ,Point > > > pq;
    int dis[maxn];
    int vis[maxn];
    void dij(int lx,int rx,int ly,int ry,int x,int y){//最短路,处理(x,y)到(lx~rx,ly~ry)矩形里的最短路
    	for(int i=lx;i<=rx;i++)for(int j=ly;j<=ry;j++)dis[id(Point(i,j))]=INF,vis[id(Point(i,j))]=0;
    	dis[id(Point(x,y))]=0;
    	pq.push(mp(0,Point(x,y)));
    	while(!pq.empty()){
    		Point u=pq.top().se;int d=pq.top().fi;pq.pop();
    		if(d!=dis[id(u)]||vis[id(u)])continue;
    		vis[id(u)]=true;
    		for(int i=head[id(u)];i;i=e[i].nxt){
    			Point v=e[i].to;
    			if(in(v,lx,rx,ly,ry)&&dis[id(u)]+e[i].w<dis[id(v)]){
    				dis[id(v)]=dis[id(u)]+e[i].w;
    				pq.push(mp(dis[id(v)],v));
    			}
    		}
    	}
    }
    void solve(int lx,int rx,int ly,int ry,int l,int r){//(lx~rx,ly~ry)表示当前处理的矩形,x~y表示我们当前处理的询问的范围
    	if(lx==rx&&ly==ry){
    		for(int i=l;i<=r;i++)ans[q[i].id]=0;
    		return ;
    	}
    	if(l>r)return;
    	if(rx-lx>=ry-ly){//我们选择切矩形较长的一边
    		int mid=(lx+rx)>>1;
    		for(int i=ly;i<=ry;i++){//枚举中线上的点
    			dij(lx,rx,ly,ry,mid,i);
    			for(int j=l;j<=r;j++)ans[q[j].id]=min(ans[q[j].id],dis[id(q[j].x)]+dis[id(q[j].y)]);//更新答案
    		}
    		int top1=0,top2=0;
    		for(int i=l;i<=r;i++){
                //将不跨过中线的点分治处理
    			if(in(q[i].x,lx,mid,ly,ry)&&in(q[i].y,lx,mid,ly,ry))q1[++top1]=q[i];
    			if(in(q[i].x,mid+1,rx,ly,ry)&&in(q[i].y,mid+1,rx,ly,ry))q2[++top2]=q[i];
    		}
    		for(int i=l;i<=l+top1-1;i++)q[i]=q1[i-l+1];
    		for(int i=l+top1;i<=l+top1+top2-1;i++)q[i]=q2[i-l-top1+1];
            //分治切开的两个矩形
    		solve(lx,mid,ly,ry,l,l+top1-1);solve(mid+1,rx,ly,ry,l+top1,l+top1+top2-1);
    	}else{
            //与上面同理
    		int mid=(ly+ry)>>1;
    		for(int i=lx;i<=rx;i++){
    			dij(lx,rx,ly,ry,i,mid);
    			for(int j=l;j<=r;j++)ans[q[j].id]=min(ans[q[j].id],dis[id(q[j].x)]+dis[id(q[j].y)]);
    		}
    		int top1=0,top2=0;
    		for(int i=l;i<=r;i++){
    			if(in(q[i].x,lx,rx,ly,mid)&&in(q[i].y,lx,rx,ly,mid))q1[++top1]=q[i];
    			if(in(q[i].x,lx,rx,mid+1,ry)&&in(q[i].y,lx,rx,mid+1,ry))q2[++top2]=q[i];
    		}
    		for(int i=l;i<=l+top1-1;i++)q[i]=q1[i-l+1];
    		for(int i=l+top1;i<=l+top1+top2-1;i++)q[i]=q2[i-l-top1+1];
    		solve(lx,rx,ly,mid,l,l+top1-1);solve(lx,rx,mid+1,ry,l+top1,l+top1+top2-1);
    	}
    }
    int main(){
    	//freopen(".in","r",stdin);
    	//freopen(".out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<m;j++){
    			int x;scanf("%d",&x);
    			addedge(Point(i,j),Point(i,j+1),x);
    			addedge(Point(i,j+1),Point(i,j),x);
    			
    		}
    	}	
    	for(int i=1;i<n;i++){
    		for(int j=1;j<=m;j++){
    			int x;scanf("%d",&x);
    			addedge(Point(i,j),Point(i+1,j),x);
    			addedge(Point(i+1,j),Point(i,j),x);
    		}
    	}	
    	scanf("%d",&Q);
    	for(int i=1;i<=Q;i++){
    		Point x,y;
    		scanf("%d%d%d%d",&x.x,&x.y,&y.x,&y.y);
    		q[i].x=x,q[i].y=y,q[i].id=i;
    	}
    	memset(ans,0x3f,sizeof(ans));
    	solve(1,n,1,m,1,Q);
    	for(int i=1;i<=Q;i++)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    python:递归函数(汉诺塔)
    python:代码复用与函数递归
    unity接入平台sdk
    原型和原型链
    闭包js
    微信小游戏的排行榜重点
    微信简单的排行榜
    代理服务器出现问题解决方案
    nodejs的fs模块
    nodejs的l分数
  • 原文地址:https://www.cnblogs.com/pmt2018/p/11644666.html
Copyright © 2011-2022 走看看