POJ-3109 Inner Vertices (扫描线 + 离散化 + 树状数组)

Inner Vertices
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 3253 Accepted: 954
Case Time Limit: 2000MS
Description

There is an infinite square grid. Some vertices of the grid are black and other vertices are white.

A vertex V is called inner if it is both vertical-inner and horizontal-inner. A vertex V is called horizontal-inner if there are two such black vertices in the same row that V is located between them. A vertex V is called vertical-inner if there are two such black vertices in the same column that V is located between them.

On each step all white inner vertices became black while the other vertices preserve their colors. The process stops when all the inner vertices are black.

Write a program that calculates a number of black vertices after the process stops.

Input

The first line of the input file contains one integer number n (0 ≤ n ≤ 100 000) — number of black vertices at the beginning.

The following n lines contain two integer numbers each — the coordinates of different black vertices. The coordinates do not exceed 109 by their absolute values.

Output

Output the number of black vertices when the process stops. If the process does not stop, output -1.

Sample Input

4
0 2
2 0
-2 0
0 -2
Sample Output

5
Hint在这里插入图片描述
Source
Northeastern Europe 2005, Northern Subregion

棋盘上有n(0<=n<=1e5)个黑点,给出n个黑点的x,y(|x|<=1e9,|y|<=1e9),每个黑点的位置不同。
对于棋盘上的白点。如果它的上下左右都存在黑点,则把这个白点染黑。问最后有多少个黑点
横纵坐标±1e9太大,而点只有1e5个,所以离散化坐标到1<=x<=1e5,1<=y<=1e5,接下来的操作都在离散化后的图上进行操作。
新增的黑点不会再新增黑点。增加的黑点数目原来的图和离散化后的图一样。
1e5个点按照y坐标从小到大排序,y一样时按照x从小到大排序。对于棋盘上的每一行,我们可以知道该行(l,r)的区间两端有黑点。
1e5个点按照x坐标从小到大排序,x一样时按照y从小到大排序。对于棋盘上的每一列,我们可以知道该行(d,u)的区间两端有黑点。
对于纵坐标坐标为y的行,区间为(l,r),对于横坐标为x的列,区间为(d,u)。如果l<x<r && d<y<u,则(x,y)应该染黑
枚举所有可能是O(n^2)的复杂度,会超时。所以x从小到大枚举列。对于每一列的情况,用flag数组来表示这一列上的点是否横向在两个黑点中间(1表示两边存在黑点)。然后统计flag的区间(u,d)里有多少个1。flag上的每一个点会随着x的变化进入会离开该行(l,r)区间。(u,d)也在不断的改变。所以就是单点修改,区间查询的问题。用树状数组解决。
判断进入或者离开区间(li,ri),就把所有的(li,ri)分别按照 li 和 ri 进行排序。如果x>li就进入区间i,如果x>ri就离开区间i。
存在原来就是黑点的情况,就不用再次染黑,注意去重。

#include<algorithm>
#include<set>
#include<iostream>
#include<stdio.h>
using namespace std;
#define ll long long
const int N = 1e5+10;
#define INT_MAX 2147483647
#define INT_MIN -2147483648

struct node{
    // node(int _x = 0,int _y = 0):x(_x),y(_y){}
    int x,y,mx,my;
}a[N];

struct seg{
    int l,r,y;
}row[N],col[N],st[N],ed[N];

bool cmp1(const node &left,const node &right)
{
    return left.my == right.my?left.mx < right.mx:left.my < right.my;
}

bool cmp2(const node &left,const node &right)
{
    return left.mx == right.mx?left.my < right.my:left.mx < right.mx;
}

bool cmpx(const node &left,const node &right)
{
    return left.x < right.x;
}

bool cmpy(const node &left,const node &right)
{
    return left.y < right.y;
}

bool cmpl(const seg &left,const seg &right)
{
    return left.l < right.l;
}

bool cmpr(const seg &left,const seg &right)
{
    return left.r < right.r;
}

struct BIT{
    int tree[N];
    int lowbit(int x){return x & -x;}
    void add(int x,int v)
    {
        while (x < N)
        {
            tree[x] += v;
            x += lowbit(x);
        }
    }
    ll query(int x,int y)
    {
        ll ans = 0;
        while (y)
        {
            ans += tree[y];
            y -= lowbit(y);
        }
        while (x)
        {
            ans -= tree[x];
            x -= lowbit(x);
        }
        return ans;
    }
}T;

int main()
{
    int n;
    scanf("%d",&n);
    for (int i = 1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
    //离散化
    sort(a+1,a+n+1,cmpx);
    for (int i = 1,last = INT_MIN,cnt = 0;i<=n;i++)
    {
        if (a[i].x != last) cnt++;
        a[i].mx = cnt;
        last = a[i].x;
    }
    sort(a+1,a+n+1,cmpy);
    for (int i = 1,last = INT_MIN,cnt = 0;i<=n;i++)
    {
        if (a[i].y != last) cnt++;
        a[i].my = cnt;
        last = a[i].y;
    }
    sort(a+1,a+n+1,cmp1);
    //求得每一行被黑点包含的区间
    int lenrow = 1;
    row[lenrow].r = INT_MAX;
    for (int i = 1,last = INT_MAX;i<=n;i++)
    {
        if (last == a[i].my) row[lenrow].r = a[i].mx;
        else
        {
            last = a[i].my;
            if (row[lenrow].r == INT_MAX) lenrow--;
            row[++lenrow].l = a[i].mx;
            row[lenrow].r = INT_MAX;
            row[lenrow].y = a[i].my;
        }
    }
    if (row[lenrow].r == INT_MAX) lenrow--;
    //区间按照左端点排序
    sort(row+1,row + lenrow + 1,cmpl);
    for (int i = 1;i<=lenrow;i++) st[i] = row[i];
    //区间按照右端点排序
    sort(row+1,row + lenrow + 1,cmpr);
    for (int i = 1;i<=lenrow;i++) ed[i] = row[i];
    sort(a+1,a+n+1,cmp2);
    int last = INT_MIN,sty = INT_MIN,edy = INT_MAX,pst = 1,ped = 1;
    ll ans = n;
    bool flag[N] = {};
    //从左到右扫描线,得出每一列的结果
    for (int i = 1;i<=n;i++)
    {
        if (last == a[i].mx)
        {
            if (flag[a[i].my] && a[i+1].mx == last) ans--;//该点已经被黑点覆盖
            edy = a[i].my;
        }
        else
        {
            if (edy != INT_MAX) ans += T.query(sty,edy-1);//该列存在被黑点包含的区间
            last = a[i].mx;
            while (st[pst].l <= last && pst <= lenrow)//判断是否进入在纵坐标为y的行的区间
            {
                T.add(st[pst].y,1);
                flag[st[pst].y] = 1;
                pst++;
            }
            while (ed[ped].r <= last && ped <= lenrow)//判断是否离开在纵坐标为y的行的区间
            {
                T.add(ed[ped].y,-1);
                flag[ed[ped].y] = 0;
                ped++;
            }
            sty = a[i].my;
            edy = INT_MAX;
        }
    }
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值