zoukankan      html  css  js  c++  java
  • 【BZOJ4456】[ZJOI2016] 旅行者(神奇的分治)

    点此看题面

    大致题意: 一张网格图,每相邻两个格子之间都有一条带权无向边,每次询问两点间的最短路。

    套路?

    听说有一个套路:网格图的题目一般都可以用分治去搞。

    emmm 几乎从没见过网格图的题,当然更不知道有这样的套路啦。。。

    分治

    考虑每次处理一个矩形区域,我们连接长边的中点,就可以把矩形分成两部分(连接长边中点是为了使中线长尽量短,等于短边)。

    然后就会发现,询问被分成了两类:

    • 两点在不同部分:显然它们的最短路必然经过中线上至少一个点。那我们只要枚举中线上的点,分别求出它只经过当前矩形内的点(因为矩形外的点肯定已经在之前被处理过了)到所有点的最短路,就可以求出答案了。
    • 两点在同一部分:它们的最短路可能不经过中线(可以递归搞),可能经过中线(和上面一样的方式搞)。

    递归时就是把两点在同一部分的询问分别分到中线两边的矩形中,如果你写过整体二分就会发现两者是非常相似的。

    由上可见,其实知道套路之后,这题就非常简单了。但我显然不知道有这种神奇的套路啊。

    至于复杂度,令(S=nm),显然短边最长也就是(O(sqrt S)),而分治复杂度是(O(logS))的,因此总复杂度为(O(Ssqrt Slog S))

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define NM 20000
    #define Q 100000
    #define INF 2147483647
    #define P(x,y) (((x)-1)*m+(y))
    #define Gmin(x,y) (x>(y)&&(x=(y)))
    using namespace std;
    const int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
    int n,m,Qt,w[NM+5][4],ans[Q+5];struct Qry {int p,x1,y1,x2,y2;}q[Q+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    		#undef D
    }F;
    class Dijkstra
    {
    	private:
    		#define mp make_pair
    		#define fir first
    		#define sec second
    		int d[NM+5],vis[NM+5];typedef pair<int,int> Pr;
    		priority_queue<Pr,vector<Pr>,greater<Pr> > q;
    	public:
    		I int operator [] (CI x) Con {return d[x];}
    		I void Dij(CI sx,CI sy,CI l1,CI r1,CI l2,CI r2)//Dijkstra求单源最短路
    		{
    			RI i,j;for(i=l1;i<=r1;++i) for(j=l2;j<=r2;++j) d[P(i,j)]=INF,vis[P(i,j)]=0;//初始化
    			q.push(mp(d[P(sx,sy)]=0,P(sx,sy)));RI x,y,nx,ny;Pr k;W(!q.empty())
    			{
    				if(k=q.top(),q.pop(),vis[k.sec]) continue;vis[k.sec]=1;
    				for(!(y=k.sec%m)&&(y=m),x=(k.sec-y)/m+1,i=0;i^4;++i)//转化回坐标,枚举四个方向
    				{
    					if((nx=x+dx[i])<l1||nx>r1||(ny=y+dy[i])<l2||ny>r2) continue;//若不在范围内
    					d[P(nx,ny)]>k.fir+w[P(x,y)][i]&&
    						(q.push(mp(d[P(nx,ny)]=k.fir+w[P(x,y)][i],P(nx,ny))),0);
    				}
    			}
    		}
    }D;
    Qry sl[Q+5],sr[Q+5];I void Solve(CI l1,CI r1,CI l2,CI r2,CI l,CI r)//分治
    {
    	RI i,j,mid,tl=0,tr=0;if(l>r||(l1==r1&&l2==r2)) return;//边界,无须再处理
    	if(r1-l1<=r2-l2)//比较哪条边短
    	{
    		for(mid=l2+r2>>1,i=l1;i<=r1;++i) for(D.Dij(i,mid,l1,r1,l2,r2),//枚举中线上的点跑最短路
    			j=l;j<=r;++j) Gmin(ans[q[j].p],D[P(q[j].x1,q[j].y1)]+D[P(q[j].x2,q[j].y2)]);//枚举询问更新答案
    		for(i=l;i<=r;++i)//把询问分到两个小矩形中
    			q[i].y1<=mid&&q[i].y2<=mid&&(sl[++tl]=q[i],0),//若两点都在第一个矩形
    			q[i].y1>mid&&q[i].y2>mid&&(sr[++tr]=q[i],0);//若两点都在第二个矩形
    		for(i=1;i<=tl;++i) q[l+i-1]=sl[i];for(i=1;i<=tr;++i) q[l+tl+i-1]=sr[i];//把询问存回原数组
    		Solve(l1,r1,l2,mid,l,l+tl-1),Solve(l1,r1,mid+1,r2,l+tl,l+tl+tr-1);//递归
    	}
    	else//和上面相似,不重复解释了
    	{
    		for(mid=l1+r1>>1,i=l2;i<=r2;++i) for(D.Dij(mid,i,l1,r1,l2,r2),
    			j=l;j<=r;++j) Gmin(ans[q[j].p],D[P(q[j].x1,q[j].y1)]+D[P(q[j].x2,q[j].y2)]);
    		for(i=l;i<=r;++i)
    			q[i].x1<=mid&&q[i].x2<=mid&&(sl[++tl]=q[i],0),
    			q[i].x1>mid&&q[i].x2>mid&&(sr[++tr]=q[i],0);
    		for(i=1;i<=tl;++i) q[l+i-1]=sl[i];for(i=1;i<=tr;++i) q[l+tl+i-1]=sr[i];
    		Solve(l1,mid,l2,r2,l,l+tl-1),Solve(mid+1,r1,l2,r2,l+tl,l+tl+tr-1);
    	}
    }
    int main()
    {
    	RI i,j;for(F.read(n,m),i=1;i<=n;w[P(i,m)][0]=w[P(i,m-1)][1],++i)
    		for(j=1;j^m;++j) F.read(w[P(i,j)][1]),w[P(i,j)][0]=w[P(i,j-1)][1];
    	for(i=1;i^n;++i) for(j=1;j<=m;++j) F.read(w[P(i,j)][3]),w[P(i,j)][2]=w[P(i-1,j)][3];
    	for(j=1;j<=m;++j) w[P(n,j)][2]=w[P(n-1,j)][3];
    	for(F.read(Qt),i=1;i<=Qt;++i) F.read(q[i].x1,q[i].y1,q[i].x2,q[i].y2),ans[q[i].p=i]=INF;
    	for(Solve(1,n,1,m,1,Qt),i=1;i<=Qt;++i) F.writeln(ans[i]);return F.clear(),0;
    }
    
  • 相关阅读:
    移植thinkPHP的dump()函数
    PHP生成linux命令行进度条
    没有ORM库的时候,通过PDO连接MySQL的方法
    mysql json字符串 解析成对应字段
    linux上安装并启动nginx
    linux上启动redis
    mui的input搜索框里的清除按钮的点击监听事件
    miniui 修改input样式及弹出框按钮文字
    js 删除数组元素的方法
    miniui反选
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4456.html
Copyright © 2011-2022 走看看