Zoning Houses(线段树暴力)

NAIPC2018 - K

Zoning Houses

Given a registry of all houses in your state or province, you would like to know the minimum size of an axis-aligned square zone such that every house in a range of addresses lies in the zone or on its border. The zoning is a bit lenient and you can ignore any one house from the range to make the zone smaller.

The addresses are given as integers from 1…n. Zoning requests are given as a consecutive range of houses. A valid zone is the smallest axis-aligned square that contains all of the points in the range, ignoring at most one.

Given the (x,y) locations of houses in your state or province, and a list of zoning requests, you must figure out for each request: What is the length of a side of the smallest axis-aligned square zone that contains all of the houses in the zoning request, possibly ignoring one house?

Input

Each input will consist of a single test case. Note that your program may be run multiple times on different inputs. Each test case will begin with a line containing two integers n and q (1≤n,q≤105), where n is the number of houses, and q is the number of zoning requests.

The next n lines will each contain two integers, x and y (−109≤x,y≤109), which are the (x,y) coordinates of a house in your state or province. The address of this house corresponds with the order in the input. The first house has address 1, the second house has address 2, and so on. No two houses will be at the same location.

The next q lines will contain two integers a and b (1≤a<b≤n), which represents a zoning request for houses with addresses in the range [a…b] inclusive.

Output

Output q lines. On each line print the answer to one of the zoning requests, in order: the side length of the smallest axis-aligned square that contains all of the points of houses with those addresses, if at most one house can be ignored.

Sample Input 1

3 2
1 0
0 1
1000 1
1 3
2 3

Sample Output 1

1
0

Sample Input 2

4 2
0 0
1000 1000
300 300
1 1
1 3
2 4

Sample Output 2

300
299

分析

题意是给出N(1e5)个点(x,y),求一个最小的正方形能够覆盖[a,b]区间(不是坐标范围[a,b],是点的id[a,b])内的点。询问有q(1e5)次,可以最暴力的找出最上面,最下面,最左边,最右边四个点的坐标,然后分别计算删掉这四个点的答案。找点和计算答案都用线段树来维护。

参考代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#define left rt<<1
#define right rt<<1|1
#define MAXN 100005
using namespace std;

int n,q,aa,bb;
int U,D,L,R;
struct node {
    int x,y;
}a[MAXN];

int cnt=0;
struct segTree {
    int le,ri;
    int u,d,l,r,pu,pd,pl,pr;
}t[MAXN<<2];

void pushup(int rt) {
    if (t[left].u>t[right].u) {
        t[rt].u=t[left].u;
        t[rt].pu=t[left].pu;
    } else {
        t[rt].u=t[right].u;
        t[rt].pu=t[right].pu;
    }
    if (t[left].r>t[right].r) {
        t[rt].r=t[left].r;
        t[rt].pr=t[left].pr;
    } else {
        t[rt].r=t[right].r;
        t[rt].pr=t[right].pr;
    }
    if (t[left].d<t[right].d) {
        t[rt].d=t[left].d;
        t[rt].pd=t[left].pd;
    } else {
        t[rt].d=t[right].d;
        t[rt].pd=t[right].pd;
    }
    if (t[left].l<t[right].l) {
        t[rt].l=t[left].l;
        t[rt].pl=t[left].pl;
    } else {
        t[rt].l=t[right].l;
        t[rt].pl=t[right].pl;
    }
}

// 建树
void build(int rt,int l,int r) {
    t[rt].le=l; t[rt].ri=r;
    t[rt].u=t[rt].r=-2e9; t[rt].d=t[rt].l=2e9;
    if (l==r) {
        t[rt].u=t[rt].d=a[l].y;
        t[rt].l=t[rt].r=a[l].x;
        t[rt].pu=t[rt].pd=t[rt].pl=t[rt].pr=++cnt;
        return;
    }
    int m=(l+r)>>1;
    build(left,l,m);
    build(right,m+1,r);
    pushup(rt);
}

// 计算y坐标最大值,实际上可将四个合并为一个函数,下同
int query_up(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].u;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=-2e9;
    if (L<=m) ret=max(ret,query_up(left,L,R));
    if (R>m) ret=max(ret,query_up(right,L,R));
    return ret;
}

int query_down(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].d;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=2e9;
    if (L<=m) ret=min(ret,query_down(left,L,R));
    if (R>m) ret=min(ret,query_down(right,L,R));
    return ret;
}

int query_right(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].r;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=-2e9;
    if (L<=m) ret=max(ret,query_right(left,L,R));
    if (R>m) ret=max(ret,query_right(right,L,R));
    return ret;
}

int query_left(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].l;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=2e9;
    if (L<=m) ret=min(ret,query_left(left,L,R));
    if (R>m) ret=min(ret,query_left(right,L,R));
    return ret;
}

// 计算最上面点的坐标,下同 
int query_pu(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].pu;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=-2e9,ans=0,tmp;
    if (L<=m) {
        tmp=query_pu(left,L,R);
        if (a[tmp].y>ret) {
            ret=a[tmp].y;
            ans=tmp;
        }
    }
    if (R>m) {
        tmp=query_pu(right,L,R);
        if (a[tmp].y>ret) {
            ret=a[tmp].y;
            ans=tmp;
        }
    }
    return ans;
}

int query_pd(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].pd;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=2e9,ans=0,tmp;
    if (L<=m) {
        tmp=query_pd(left,L,R);
        if (a[tmp].y<ret) {
            ret=a[tmp].y;
            ans=tmp;
        }
    }
    if (R>m) {
        tmp=query_pd(right,L,R);
        if (a[tmp].y<ret) {
            ret=a[tmp].y;
            ans=tmp;
        }
    }
    return ans;
}

int query_pr(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].pr;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=-2e9,ans=0,tmp;
    if (L<=m) {
        tmp=query_pr(left,L,R);
        if (a[tmp].x>ret) {
            ret=a[tmp].x;
            ans=tmp;
        }
    }
    if (R>m) {
        tmp=query_pr(right,L,R);
        if (a[tmp].x>ret) {
            ret=a[tmp].x;
            ans=tmp;
        }
    }
    return ans;
}

int query_pl(int rt,int L,int R) {
    if (L<=t[rt].le && t[rt].ri<=R)
        return t[rt].pl;
    int m=(t[rt].le+t[rt].ri)>>1;
    int ret=2e9,ans=0,tmp;
    if (L<=m) {
        tmp=query_pl(left,L,R);
        if (a[tmp].x<ret) {
            ret=a[tmp].x;
            ans=tmp;
        }
    }
    if (R>m) {
        tmp=query_pl(right,L,R);
        if (a[tmp].x<ret) {
            ret=a[tmp].x;
            ans=tmp;
        }
    }
    return ans;
}

// 计算删掉某个点后的上下左右边界
void cal(int p) {
    U=R=-2e9; L=D=2e9;
    if (p-1>=aa) {
        U=query_up(1,aa,p-1);
        D=query_down(1,aa,p-1);
        L=query_left(1,aa,p-1);
        R=query_right(1,aa,p-1);
    }
    if (p+1<=bb) {
        U=max(U,query_up(1,p+1,bb));
        D=min(D,query_down(1,p+1,bb));
        L=min(L,query_left(1,p+1,bb));
        R=max(R,query_right(1,p+1,bb));
    }
}

int main() {
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) {
        scanf("%d%d",&a[i].x,&a[i].y);
    }
    build(1,1,n);
    for (int i=1;i<=q;i++) {
        scanf("%d%d",&aa,&bb);
        int PU,PD,PL,PR,ANS=0;
        // 分别计算去掉四个点后的答案,取最小的一个
        PU=query_pu(1,aa,bb); cal(PU); ANS=max(U-D,R-L);
        PD=query_pd(1,aa,bb); cal(PD); ANS=min(ANS,max(U-D,R-L));
        PL=query_pl(1,aa,bb); cal(PL); ANS=min(ANS,max(U-D,R-L));
        PR=query_pr(1,aa,bb); cal(PR); ANS=min(ANS,max(U-D,R-L));
        printf("%d\n",ANS);
    }
    return 0;
}
发布了136 篇原创文章 · 获赞 33 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Radium_1209/article/details/100120665