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;
    }
    
  • 相关阅读:
    EntityFramework优缺点
    领导者与管理者的区别
    七个对我最好的职业建议(精简版)
    The best career advice I’ve received
    Difference between Stored Procedure and Function in SQL Server
    2015年上半年一次通过 信息系统项目管理师
    Difference between WCF and Web API and WCF REST and Web Service
    What’s the difference between data mining and data warehousing?
    What is the difference between a Clustered and Non Clustered Index?
    用new创建函数的过程发生了什么
  • 原文地址:https://www.cnblogs.com/pmt2018/p/11644666.html
Copyright © 2011-2022 走看看