BZOJ 1514 _ [POI2006]ZAB-Frogs 单调队列+二分BFS

来源:互联网 发布:php 生成订单号 编辑:程序博客网 时间:2024/05/18 17:42

题意:
给定一个网格图,其中有一些坏点,要求使起点到终点的路径上的所有点到离该点最近的坏点的最小距离距离最大,求这个最大值。
解析:
读完题显然分为两部分:
第一部分:预处理所有点到他最近的坏点的距离。
第二部分:二分最大距离bfs判定。
第二部分不用说吧?
主要就是卡在第一部分。
我们考虑按照每列来计算每个点的dis距离(即到他最近的坏点的距离)
显然可以发现,对于该列来说,每一行都可能有一个到该列最近的点,并且我们发现,如果某一行有两个坏点的话,假设分别为A,B,并且A到该列的距离最近,那么B显然不会对这一列的dis有任何影响。
所以我们显然可以在求之前预处理一下每一行的如果存在坏点的那个最近的坏点的坐标。
接下来,我们讨论坏点k,l,设我们要更新的点是(x,y)
如果k优于l,那么我们不妨列一下式子。

(Xkx)2+(Yky)2<=(Xlx)2+(Yly)2


X2kX2l+2xXl2xXkYkYl+Yk+Yl<=2y

所以我们只需要按处理出来的坏点顺序维护一个X2kX2l+2xXl2xXkYkYl+Yk+Yl递增即可。
更新的时候每一次在其中二分查找最后一个<=2y的。
复杂度O(n*m*logn)
代码:

#include <queue>#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 1100using namespace std;int n,m;int num;int calc[N][N];int xx[]={0,1,-1,0,0};int yy[]={0,0,0,-1,1};struct node{    int x,y;    node(){}    node(int _x,int _y):x(_x),y(_y){}    friend istream& operator >> (istream &_,node &a)    {scanf("%d%d",&a.x,&a.y);calc[a.y][++calc[a.y][0]]=a.x;return _;}    friend bool operator == (node a,node b)    {return a.x==b.x&&a.y==b.y;}    node operator - (const node &a)    {return node(x-a.x,y-a.y);}    int operator * (const node &a)    {return x*a.x+y*a.y;}}pt[N*N],st,ed;int dis[N][N];int f[N][N];node q[N];node newq[N];void init(){    for(int i=1;i<=m;i++)        sort(calc[i]+1,calc[i]+calc[i][0]+1);}int get_dis(node a,node b){    return (a-b)*(a-b);}double q_calc(node a,node b,int tmpx){    return (double)(a.x*a.x-b.x*b.x+2*tmpx*b.x-2*tmpx*a.x)/(a.y-b.y)+a.y+b.y;}void get_min_dis(){    memset(dis,0x3f,sizeof(dis));    for(int i=1;i<=num;i++)        dis[pt[i].x][pt[i].y]=0;    for(int i=1;i<=n;i++)    {        int l=1,r=0;        for(int j=1;j<=m;j++)        {            int ll=1,rr=calc[j][0],ans=ll;            while(ll<=rr)            {                int mid=(ll+rr)>>1;                if(calc[j][mid]<=i)ans=mid,ll=mid+1;                else rr=mid-1;            }            if(ans+1<=calc[j][0]&&abs(calc[j][ans+1]-i)<abs(calc[j][ans]-i))                ans++;            if(ans<=calc[j][0])            {                node tmp;                tmp.x=calc[j][ans],tmp.y=j;                q[++r]=tmp;            }        }        if(r==1)        {            for(int j=1;j<=m;j++)                dis[i][j]=get_dis(node(i,j),q[r]);            continue;        }        int tmpr=r;        l=1,r=0;        for(int j=1;j<=tmpr;j++)        {            while(l<r&&q_calc(q[j],newq[r],i)<=q_calc(newq[r],newq[r-1],i))r--;            if(l>=r||q_calc(q[j],newq[r],i)>q_calc(newq[r],newq[r-1],i))                newq[++r]=q[j];        }        for(int j=1;j<=m;j++)        {            int ll=l+1,rr=r,ans=1;            while(ll<=rr)            {                int mid=(ll+rr)>>1;                if(q_calc(newq[mid],newq[mid-1],i)<=2*j)ans=mid,ll=mid+1;                else rr=mid-1;            }            dis[i][j]=min(get_dis(node(i,j),newq[ans]),dis[i][j]);        }    }   }bool check(int x){    memset(f,0,sizeof(f));    queue<node>que;    if(dis[st.x][st.y]>=x)        que.push(st),f[st.x][st.y]=1;    while(!que.empty())    {        node u=que.front();        que.pop();        for(int i=1;i<=4;i++)        {            node tmp;            tmp.x=u.x+xx[i],tmp.y=u.y+yy[i];            if(tmp.x<=0||tmp.x>n||tmp.y<=0||tmp.y>m)continue;            if(dis[tmp.x][tmp.y]>=x&&!f[tmp.x][tmp.y])            {                f[tmp.x][tmp.y]=1;                que.push(tmp);            }        }    }    return f[ed.x][ed.y];}int main(){    #ifndef ONLINE_JUDGE        freopen("zab5ocen.in","r",stdin);        freopen("1514.out","w",stdout);    #endif        scanf("%d%d",&n,&m);        scanf("%d%d%d%d",&st.x,&st.y,&ed.x,&ed.y);        scanf("%d",&num);        for(int i=1;i<=num;i++)cin>>pt[i];        init();         get_min_dis();        int l=0,r=n*n+m*m,ans;        while(l<=r)        {            int mid=(l+r)>>1;            if(check(mid))l=mid+1,ans=mid;            else r=mid-1;        }        printf("%d\n",ans);    #ifndef ONLINE_JUDGE        fclose(stdin);fclose(stdout);    #endif    return 0;}
0 0
原创粉丝点击