bzoj1514: [POI2006]ZAB-Frogs

传送门
显然在求出最短距离之后是可以二分答案的。
我们考虑按照每列来计算每个点的最短距离。
显然可以发现,对于该列来说,每一行都可能有一个到该列最近的点,并且我们发现,如果某一行有两个坏点的话,假设分别为A,B,并且A到该列的距离最近,那么B显然不会对这一列的dis有任何影响。
所以我们显然可以在求之前预处理一下每一行的如果存在坏点的那个最近的坏点的坐标。
接下来,我们讨论坏点k,l,设我们要更新的点是(x,y)
如果k优于l,那么我们不妨列一下式子。

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

X2k2Xkx+Y2k2Yky<=X2l2Xlx+Y2l2Yly

X2kX2l2Xkx+2XlxYkYl+Yk+Yl<=2y

二分最后一个<=2y的即可。

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 1100
using namespace std;
int n,m,num,c[N][N];
int d[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
struct node{
    int x,y;
    node(){}
    node(int _x,int _y):x(_x),y(_y){}
    friend bool operator ==(node a,node b){
        return a.x==b.x&&a.y==b.y;
    }
}a[N*N],S,T;
int dis[N][N];
bool vis[N][N];
node q[N],nq[N];
int ask(node a,node b){
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
double slop(node a,node b,int tmpx){
    return 1.0*(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 pre(){
    memset(dis,60,sizeof(dis));
    for (int i=1;i<=num;i++)
        dis[a[i].x][a[i].y]=0;
    for (int i=1;i<=n;i++){
        int h=1,t=0;
        for (int j=1;j<=m;j++){
            int l=1,r=c[j][0],ans=l;
            while (l<=r){
                int mid=(l+r)/2;
                if (c[j][mid]<=i) ans=mid,l=mid+1;
                else r=mid-1;
            }
            if (ans+1<=c[j][0]&&abs(c[j][ans+1]-i)<abs(i-c[j][ans]))
                ans++;
            if (ans<=c[j][0]){
                node tmp;
                tmp.x=c[j][ans],tmp.y=j;
                q[++t]=tmp;
            }
        }
        if (t==1){
            for (int j=1;j<=m;j++)
                dis[i][j]=ask(node(i,j),q[t]);
            continue;
        }
        int T=t; t=0;
        for (int j=1;j<=T;j++){
            while (h<t&&slop(q[j],nq[t],i)<=slop(nq[t],nq[t-1],i)) t--;
            if (h>=t||slop(q[j],nq[t],i)>slop(nq[t],nq[t-1],i))
                nq[++t]=q[j];
        }
        for (int j=1;j<=m;j++){
            int l=2,r=t,ans=1;
            while (l<=r){
                int mid=(l+r)/2;
                if (slop(nq[mid],nq[mid-1],i)<=2*j)
                    ans=mid,l=mid+1;
                else r=mid-1;
            }
            dis[i][j]=min(dis[i][j],ask(node(i,j),nq[ans]));
        }
    }
}
bool check(int x){
    memset(vis,0,sizeof(vis));
    queue<node> q;
    while (!q.empty()) q.pop();
    if (dis[S.x][S.y]>=x)
        q.push(S),vis[S.x][S.y]=1;
    while (!q.empty()){
        node u=q.front(); q.pop();
        for (int i=0;i<4;i++){
            node tmp;
            tmp.x=u.x+d[i][0];
            tmp.y=u.y+d[i][1];
            if (tmp.x<1||tmp.x>n||tmp.y<1||tmp.y>m) continue;
            if (dis[tmp.x][tmp.y]>=x&&!vis[tmp.x][tmp.y]){
                vis[tmp.x][tmp.y]=1;
                q.push(tmp);
            }
        }
    }
    return vis[T.x][T.y];
}
int main(){
    scanf("%d%d",&n,&m);
    scanf("%d%d%d%d",&S.x,&S.y,&T.x,&T.y);
    scanf("%d",&num);
    for (int i=1;i<=num;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
        c[a[i].y][++c[a[i].y][0]]=a[i].x;
    }
    for (int i=1;i<=m;i++)
        sort(c[i]+1,c[i]+c[i][0]+1);
    pre();
    int l=0,r=n*n+m*m,ans=0;
    while (l<=r){
        int mid=(l+r)/2;
        if (check(mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值