zoukankan      html  css  js  c++  java
  • 【洛谷3438】[POI2006] ZAB-Frogs(斜率优化)

    点此看题面

    • 给定一张(n imes m)的网格图,其中有(t)个关键点。
    • 求一条从((sx,sy))((ex,ey))的路径,使得离所有关键点的最小欧几里得距离最大。
    • (n,mle10^3,tle n imes m)

    斜率优化

    考虑设(f_{i,j})表示((i,j))离所有关键点的最小欧几里得距离。

    容易写出转移式:

    [egin{align} f_{i,j}&=max{(i-x_k)^2+(j-y_k)^2}\ &=max{-2ix_k-2jy_k+x_k^2+y_k^2}+i^2+j^2 end{align} ]

    发现这是一个斜率优化的形式,虽然是二元,但却是相互独立的。

    我们从小到大枚举行(i),然后先考虑每一列在第(i)行的最优点,比较(p,q)(x_p<x_q)):

    [-2ix_p+x_p^2<-2ix_q+x_q^2\ 2i<x_p+x_q ]

    发现同列的最优决策点是单调移动的,因此我们可以很方便地求出每一列的决策点,这样一来对于某一行有用的决策点个数就是(O(m))的了。

    方便起见,令(v_k=-2ix_k+x_k^2+y_k^2),然后对于第(i)行比较(p,q)(y_p<y_q)):

    [-2jy_p+v_p<-2jy_q+v_q\ 2j<frac{v_q-v_p}{y_q-y_p} ]

    因此,我们先取出每一列的决策点建一个斜率单调递增的单调队列,然后枚举(j)求出答案即可。

    枚举答案+并查集合并

    根据(f_{i,j})((i,j))扔到对应的桶里。

    从大到小枚举答案,每次把桶中的所有位置取出,尝试与四周的方格在并查集上合并。

    当起点和终点在一个连通块中了,此时枚举到的就是答案。

    注意特判起点和终点重合的情况。

    代码:(O(nmalpha(nm)))

    #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 N 1000
    #define ID(x,y) (((x)-1)*m+(y))
    using namespace std;
    const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
    int n,m,sx,sy,ex,ey,qe[N+5][N+5],nw[N+5],ct[N+5],v[N+5],q[N+5],f[N+5][N+5];
    vector<pair<int,int> > V[N*N+5];vector<pair<int,int> >::iterator it;
    namespace U//并查集合并
    {
    	int f[N*N+5];I int fa(CI x) {return f[x]?f[x]=fa(f[x]):x;}
    	I void M(RI x,RI y) {(x=fa(x))^(y=fa(y))&&(f[x]=y);}//合并
    	I bool C(CI x,CI y) {return fa(x)==fa(y);}//判连通
    }
    int main()
    {
    	RI i,j,x,y,t;scanf("%d%d%d%d%d%d",&n,&m,&sx,&sy,&ex,&ey);
    	for(scanf("%d",&t);t;--t) scanf("%d%d",&x,&y),qe[y][++ct[y]]=x;
    	for(i=1;i<=m;++i) sort(qe[i]+1,qe[i]+ct[i]+1),nw[i]=1;//每一列的关键点按行号排序
    	RI H,T;for(i=1;i<=n;++i)
    	{
    		for(H=1,T=0,j=1;j<=m;++j) if(ct[j])
    		{
    			W(nw[j]^ct[j]&&2*i>=qe[j][nw[j]]+qe[j][nw[j]+1]) ++nw[j];//更新每一列的最优决策点
    			v[j]=-2*i*qe[j][nw[j]]+qe[j][nw[j]]*qe[j][nw[j]]+j*j;
    			W(H<T&&1LL*(v[q[T]]-v[q[T-1]])*(j-q[T])>=1LL*(v[j]-v[q[T]])*(q[T]-q[T-1])) --T;q[++T]=j;//维护斜率单调递增的单调队列
    		}
    		for(j=1;j<=m;++j)
    		{
    			W(H^T&&2LL*j*(q[H+1]-q[H])>=v[q[H+1]]-v[q[H]]) ++H;//弹出队首不优元素
    			V[f[i][j]=v[q[H]]-2*j*q[H]+i*i+j*j].push_back(make_pair(i,j));//从队首转移,扔至f[i][j]对应桶中
    		}
    	}
    	if(ID(sx,sy)==ID(ex,ey)) return printf("%d
    ",f[sx][sy]),0;//特判起点与终点相同
    	RI k,nx,ny;for(i=n*m;~i;--i)//从大到小枚举答案
    	{
    		for(it=V[i].begin();it!=V[i].end();++it) for(k=ID(it->first,it->second),j=0;j^4;++j)
    			(nx=it->first+dx[j])&&nx<=n&&(ny=it->second+dy[j])&&ny<=m&&f[nx][ny]>=i&&(U::M(k,ID(nx,ny)),0);//尝试与四周合并
    		if(U::C(ID(sx,sy),ID(ex,ey))) {printf("%d
    ",i);break;}//如果连通了,则当前枚到的就是答案
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    css浮动
    css各种元素最原始的表现
    css3 unset属性
    js类式继承
    javascript编写Tab选项卡
    javaScript事件冒泡
    javascript中的&&与||的用法
    比较好的前端网站
    原生js开发tab选项卡之闭包
    冒泡排序(中级版)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3438.html
Copyright © 2011-2022 走看看