【单调栈 二分+BFS】【POI2006】ZAB-Frogs BZOJ1514

题目描述

一群青蛙正在摧毁Byteotia所有的庄稼。一个叫Byteasar的农夫决定使用一种放在田里的奇特的“scarefrogs”来吓跑他们,所有的青蛙在跳跃过程中都尽量使自己离他们越远越好,即是让自己离最近的scarefrog越远越好。Byteasar的田是块矩形的土地。青蛙们跳跃的方向平行于坐标轴并且每次跳跃的距离为1,一条跳跃路线的scarefrogs-距离为路径上所有点距离所有scarefrogs的最近距离。Byteasar已经知道青蛙们的出发位置和目的地位置,所以他在田里放置了若干个scarefrogs。
他请求你帮忙写一个程序找一条路径使得该路径的scarefrogs-距离最大。

输入

第一行包含两个整数: wx wy 分别表示田地的宽和长 (2wx,wy1000)
第二行包含四个整数: px,py,kx,ky ,其中 (px,py) 为青蛙的起始位置, (kx,ky) 为目的地位置 (1px,kxwx,1py,kywy)
第三行一个整数 n 表示scarefrogs的总数 (1nwxwy) 。接下来 n 行描述所有scarefrogs的坐标。坐标用 xi yi 描述第 i 个 scarefrog的位置 (1xiwx,1yiwy) 。没有两个scarefrogs在同一个位置出现并且不会出现在 (px,py) (kx,ky) 上。

输出

一个整数代表目标路径scarefrog距离的平方。如果青蛙不可避免遇到某些scarefrog那么答案为0。

样例输入

5 5
1 1 5 5
2
3 3
4 2

样例输出

4

提示

【单调栈 二分+BFS】【POI2006】ZAB-Frogs BZOJ1514_第1张图片


我就是看题面格式不爽来打我呀~
然而这道题是关于Frog的(雾

一眼二分scarefrog-距离判断是否可行,想了一下两两连线判断起点终点是否在同一区域内,然而并不会写,而且这道题路径只能沿着网格走。所以只要删掉不能走的点BFS即可。
BFS不是问题,问题是处理出每个点到最近的scarefrog的距离。
采用单调队列的思想。对于每一个 x 坐标按照 y 坐标递增的顺序处理。
显而易见的是, y 坐标相同的scarefrog只取 x 坐标到当前 x 坐标最近的即可。则至多有1000只scarefrog对答案产生影响。
按照 y 坐标从小到大处理scarefrog。考虑当前的节点为 (x,y) ,两只scarefrog坐标为 (xa,ya) (xb,yb) ,则b到当前点距离小于a,当且仅当

(xbx)2+(yby)2<(xaa)2+(yay)2

括号打开,化简得
x2bx2a+2x(xaxb)ybya+ya+yb<2y

于是对上式左侧的一堆预处理一个单调栈,表示下一个更优的scarefrog。对原网格中的每个点扫过去,判断是否需要更换最近点即可。
最后二分BFS,总时间复杂度 O(nmlog2n)
UPD:
貌似单调栈并没有说明白。
单调栈中维护的是相邻两个元素的大柿子。考虑单调栈顶为 b ,表示的是 2 号和 3 号点的柿子。新来 1 号点,和 2 号点的柿子为 a
则当 2y>a 时, 1 号点比 2 号点优;当 2y<b 时, 3 号点比 2 号点优。
ab ,则 [0,a](b,2m]=[0,2m] ,也就是说 2 号点永远不可能成为最优的,所以可以将 2 号点以及代表的 b 弹栈,用 1 号点以及和栈顶 3 号点的柿子继续更新。

代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
struct pos{
    int x, y;
    pos(){}
    pos(int X, int Y):x(X), y(Y){}
    bool operator<(const pos &a)const{
        return y==a.y?xx:yy;
    }
}st, en, frog[1000010], use[1010];  // use数组表示每行要用的scarefrog
// dis:最近scarefrog的距离 pts:这行scarefrog的指针 rg:这行scarefrog的范围 bel:选中scarefrog的编号
int n, m, t, dis[1010][1010], pts[1010], rg[1010], bel[1010];
double stk[1010]; bool v[1010][1010];
queue<pos> Q;
inline int rd(){
    int a=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) (a*=10)+=c-'0', c=getchar();
    return a;
}
// 计算大柿子
double calc(int xa, int ya, int xb, int yb, int x){
    return 1.*(xa*xa-xb*xb+2*x*(xb-xa))/(ya-yb)+ya+yb;
}
int dist(int dx, int dy){
    return dx*dx+dy*dy;
}
void pre(){
    sort(frog, frog+t);
    // 处理出每一x坐标的scarefrog范围
    for(int i=0;iint p=frog[i].y; pts[p]=i;
        while(iy==p) i++;
        rg[p]=i;
    }
    // 处理dis
    for(int i=1;i<=n;i++){
        int h=0; use[h].x=1000, use[h++].y=-1000;
        for(int j=1;j<=m;j++){
            if(!rg[j]) continue;
            // 更新距离当前x坐标最近的scarefrog
            if(pts[j]+1abs(i-frog[pts[j]].x)>abs(i-frog[pts[j]+1].x))
                pts[j]++;
            use[h++]=frog[pts[j]];
        }
        int h2=0;
        for(int j=1;juse[j].x, use[j].y, use[bel[h2-1]].x, use[bel[h2-1]].y, i);
            // 维护单调栈
            while(h2&&tt<=stk[h2-1]) h2--, tt=calc(use[j].x, use[j].y, use[bel[h2-1]].x, use[bel[h2-1]].y, i);
            bel[h2]=j; stk[h2++]=tt;
        }
        int pt=0;
        for(int j=1;j<=m;j++){
            // 找到最近点
            while(pt+11]*2) pt++;
            dis[i][j]=dist(i-use[bel[pt]].x, j-use[bel[pt]].y);
        }
    }
}
bool bfs(int ex){
    memset(v, 0, sizeof v);
    Q.push(st); v[st.x][st.y]=1;
    pos w;
    while(!Q.empty()){
        w=Q.front(); Q.pop();
        if(w.x>1) if(dis[w.x-1][w.y]>=ex) if(!v[w.x-1][w.y]){v[w.x-1][w.y]=1; Q.push(pos(w.x-1, w.y));}
        if(w.xif(dis[w.x+1][w.y]>=ex) if(!v[w.x+1][w.y]){v[w.x+1][w.y]=1; Q.push(pos(w.x+1, w.y));}
        if(w.y>1) if(dis[w.x][w.y-1]>=ex) if(!v[w.x][w.y-1]){v[w.x][w.y-1]=1; Q.push(pos(w.x, w.y-1));}
        if(w.y<m) if(dis[w.x][w.y+1]>=ex) if(!v[w.x][w.y+1]){v[w.x][w.y+1]=1; Q.push(pos(w.x, w.y+1));}
    }
    return v[en.x][en.y];
}
int bin(){
    int l=0, r=min(dis[st.x][st.y], dis[en.x][en.y])+1, m;
    while(r-l!=1){
        m=(l+r)>>1;
        if(bfs(m)) l=m;
        else r=m;
    }
    return l;
}
int main(){
    n=rd(), m=rd();
    st.x=rd(), st.y=rd(); en.x=rd(), en.y=rd();
    t=rd();
    for(int i=0;ix=rd(), frog[i].y=rd();
    pre();
    // for(int i=1;i<=n;i++)
    //  for(int j=1;j<=m;j++)
    //      printf("%d%c", dis[i][j], j==m?'\n':' ');
    printf("%d\n", bin());
    return 0;
}

一开始是不知道单调栈还可以维护两两比较这种东西的……学到了。

你可能感兴趣的:(各种OI,BFS,单调栈)