1818: [Cqoi2010]内部白点
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 571 Solved: 281
[ Submit][ Status][ Discuss]
Description
无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点)。每秒钟,所有内部白点同时变黑,直到不存在内部白点为止。你的任务是统计最后网格中的黑点个数。 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 < y < y2使得(x,y1)和(x,y2)都是黑点)。
Input
输入第一行包含一个整数n,即初始黑点个数。以下n行每行包含两个整数(x,y),即一个黑点的坐标。没有两个黑点的坐标相同,坐标的绝对值均不超过109。
Output
输出仅一行,包含黑点的最终数目。如果变色过程永不终止,输出-1。
Sample Input
4
0 2
2 0
-2 0
0 -2
0 2
2 0
-2 0
0 -2
Sample Output
5
数据范围
36%的数据满足:n < = 500
64%的数据满足:n < = 30000
100%的数据满足:n < = 100000
数据范围
36%的数据满足:n < = 500
64%的数据满足:n < = 30000
100%的数据满足:n < = 100000
树状数组好题,值得一做,题意就是给若干条横线和竖线,问共有多少个点?其中点包括线段的端点和线段的交点。
怎么搞?首先我们需要对一个方向离散化,这里我对y坐标离散化了,那么我们要从上往下扫描,当扫描的竖线的上端后,我们在该竖线的纵坐标(离散化后的)上+1,表示这个位置上有一条竖线正在往下延伸,可想而知如果继续从上往下扫,下一条是横线,并且横线的左右端点[L,R]包含刚才竖线的纵坐标,那么显然横线与纵线相交了,因为此时纵线的下端点还没扫出来,必然知道纵线的下端点在横线下方(先不考虑刚好在横线上),这样上端点与下端点位于横线两端,所以相交是必然的。
这给了我们一个启示,我们从上往下扫描,如果扫到竖线的上端点,就在对应纵坐标+1,下端点则-1,横线则统计[L,R]区间的和,再回到刚才问题,竖线刚好在横线上,那么你必须给一个顺序,首先是竖线下端点,其次是横线,再其次是竖线上端点,为什么呢?如果竖线下端点在横线之后,那么我们就会先扫描横线,此时该条竖线的上端点就有可能对横线贡献一次,其实这一次显然是下端点刚好在横线,虽然是有一个交点,但这个交点本来就是线段的端点,并不是新产生的,因此没必要计算,所以先下端点在横线的话,那么在下端点插入时会-1,这样就不会统计进去了。同理可以判断横线在竖线上端点前面。
然后就是代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define Maxn 100010
using namespace std;
struct edge{
int x,l,r,d;
edge(int _x=0,int _l=0,int _r=0,int _d=0):x(_x),l(_l),r(_r),d(_d){}
bool operator<(const edge &a)const{
return x<a.x||x==a.x&&d>a.d; //x相同,纵线下端>横线>纵线上端
}
}e[Maxn*2];
struct point{
int x,y;
bool operator<(const point &a)const{
return x<a.x||x==a.x&&y<a.y;
}
void read(){
scanf("%d%d",&x,&y);
}
}p[Maxn];
bool cmp(point a,point b){
return a.y<b.y||a.y==b.y&&a.x<b.x;
}
int Y[Maxn];
int n,m;
int cal(int y){ //返回离散化后数组的元素下标+1
return lower_bound(Y,Y+m,y)-Y+1;
}
int c[Maxn];
void add(int x,int y){
while(x<=n){
c[x]+=y;
x+=x&-x;
}
}
int sum(int x){
int ans=0;
while(x){
ans+=c[x];
x-=x&-x;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++){
p[i].read();
Y[i]=p[i].y;
}
sort(Y,Y+n);
m=unique(Y,Y+n)-Y; //对Y离线化
sort(p,p+n); //先x后y
int tot=0;
for(int i=1;i<n;i++){
if(p[i].x==p[i-1].x)
e[tot++]=edge(p[i].x,cal(p[i-1].y),cal(p[i].y),0); //横线(询问)
}
sort(p,p+n,cmp); //先y后x
for(int i=1;i<n;i++){
if(p[i].y==p[i-1].y){
e[tot++]=edge(p[i-1].x,cal(p[i-1].y),0,-1); //纵线上端
e[tot++]=edge(p[i].x,cal(p[i].y),0,1); //纵线下端
}
}
sort(e,e+tot);
int ans=n;
for(int i=0;i<tot;i++){ //从上往下扫描
if(!e[i].d) //横线询问
ans+=sum(e[i].r)-sum(e[i].l-1);
else //纵线插入
add(e[i].l,-e[i].d);
}
printf("%d\n",ans);
return 0;
}