Zoning Houses(线段树暴力)

本文介绍了一种解决NAIPC2018-K竞赛中房屋分区问题的算法,目标是最小化轴对齐的正方形区域,使得指定范围内的所有房屋位于该区域内或边界上,允许忽略一个房屋。通过使用线段树维护坐标,快速计算最优正方形边长。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值