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;
}
本文来自博客园,作者:Un-Defined,转载请保留本文署名Un-Defined,并在文章顶部注明原文链接:https://www.cnblogs.com/EIPsilly/p/17463683.html