gym 101505 CTU Open Contest 2016 G Orchard Division

来源:互联网 发布:弗洛伊德算法例题 编辑:程序博客网 时间:2024/06/08 00:28

Problem

codeforces.com/gym/101505/attachments
vjudge.net/contest/187874#problem/G

Meaning

一个 m * m 的网格(长、宽下标 [ 0,m - 1])里有 n 个点,现要从四个角落中任意一个开始引出一个矩形,使得矩形内的点数恰好是总点数一半,且面积要尽量小,求这个最小面积,不存在输出 -1。

Analysis

枚举角落,以左下角为例。
从左到右枚举列(x),先一次性把该列的所有点全部加入考虑的“点集”,然后找当前的这个“点集”里按纵坐标 y 升序排的第 n2 个点的纵坐标(y),就得到一个矩形:从(0,0)到(x,y),如果这个矩形里恰好有 n2 个点,那这个矩形就是合法的候选解。
怎么维护这个“点集”可以快速找到第 n2 的点呢?可以用权值线段树。首先把所有纵坐标离散化,每加入一个点就把它的纵坐标插入到树中,就可以快速找第 n2 个点,并且可以查询那个纵坐标的区间里是否恰好有 n2 个点。
至于枚举角落,可以把整个 m * m 的大矩阵做旋转(即把点的坐标做变换),就可以把其它角落都转化为左下角。

Code

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N = 1e6, M = 1e9;struct pnt{    int x, y;    pnt(int _x = 0, int _y = 0):        x(_x), y(_y) {}    bool operator < (const pnt &rhs) const    {        return x != rhs.x ? x < rhs.x : y < rhs.y;    }} p[N];void rotate(int n, int m){    for(int i = 0, x; i < n; ++i)    {        x = p[i].x;        p[i].x = m - 1 - p[i].y;        p[i].y = x;    }}int tree[N+7<<2];inline void pushup(int o){    tree[o] = tree[o<<1] + tree[o<<1|1];}void update(int y, int l, int r, int rt){    if(l == r)    {        ++tree[rt];        return;    }    int m = l + r >> 1;    if(m < y)        update(y, m + 1, r, rt<<1|1);    else        update(y, l, m, rt<<1);    pushup(rt);}// 找第 k 个纵坐标int kth(int k, int l, int r, int rt){    if(l == r)        return l;    int m = l + r >> 1;    if(tree[rt<<1] >= k)        return kth(k, l, m, rt<<1);    else        return kth(k - tree[rt<<1], m + 1, r, rt<<1|1);}// 查询纵坐标区间的点数int query(int ql, int qr, int l, int r, int rt){    if(ql <= l && r <= qr)        return tree[rt];    int res = 0, m = l + r >> 1;    if(ql <= m)        res += query(ql, qr, l, m, rt<<1);    if(qr > m)        res += query(ql, qr, m + 1, r, rt<<1|1);    return res;}long long solve(int n, long long m){    // 离散化纵坐标    static int dsc[N];    for(int i = 0; i < n; ++i)        dsc[i] = p[i].y;    sort(dsc, dsc + n);    int ny = unique(dsc, dsc + n) - dsc;    sort(p, p + n);    memset(tree, 0, sizeof tree);    // 先直接插入前 n / 2 - 1 个点    for(int i = 0, y; i < n / 2 - 1; ++i)    {        y = lower_bound(dsc, dsc + ny, p[i].y) - dsc;        update(y, 0, ny - 1, 1);    }    long long res = m * m;    for(int i = n / 2 - 1, x, y, num; i < n; )    {        // 把当前列所有点都插入        for(x = p[i].x; i < n && p[i].x == x; ++i)        {            y = lower_bound(dsc, dsc + ny, p[i].y) - dsc;            update(y, 0, ny - 1, 1);        }        // 找第 n / 2 个纵坐标        y = kth(n>>1, 0, ny - 1, 1);        // 检查矩形里是否恰好有 n / 2 个点        num = query(0, y, 0, ny - 1, 1);        if(num << 1 == n)            res = min(res, (dsc[y] + 1LL) * (x + 1LL));    }    return res;}int main(){    int n, m;    while(~scanf("%d%d", &m, &n))    {        for(int i = 0; i < n; ++i)            scanf("%d%d", &p[i].x, &p[i].y);        // 奇数直接忽略        if(n & 1)        {            puts("-1");            continue;        }        long long ans = (long long)m * m;        for(int i = 0; i < 4; ++i)        {            ans = min(ans, solve(n, m));            // 顺时针旋转矩阵            rotate(n, m);        }        if(ans >= (long long)m * m)            ans = -1;        printf("%I64d\n", ans);    }    return 0;}
原创粉丝点击