您的位置:首页 > 其它

BZOJ 1818: [Cqoi2010]内部白点

2016-01-08 20:14 197 查看
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1818

扫描线+树状数组。

首先可以看出题目其实是求有多少点上下左右至少有一个黑点。

拿x轴离散化,对x轴排一次序,于是我们可以拿出每一条竖线,把它拆成两个事件点,一个+1,一个-1,然后再对y轴排一次序,当作一条事件线段,记下左右端点。

然后对于这些事件按y轴排一次序,然后树状数组就可以了。

#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define low(x) x&(-x)
#define maxn 100500
struct data{int x,y,r,k;
}a[maxn],s[maxn*5];
int ans,hash[maxn],t[maxn];
int n,cnt;
using namespace std;
int read(){
int x=0,f=1; char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while (isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
bool cmp(data a,data b){
if (a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
bool cmp2(data a,data b){
if (a.y==b.y) return a.x<b.x;
return a.y<b.y;
}
bool cmp3(data a,data b){
if (a.y==b.y) return a.k<b.k;
return a.y<b.y;
}
int find(int x){
int l=1,r=n;
while (l<=r){
int mid=(l+r)/2;
if (hash[mid]==x) return mid;
else if (hash[mid]<x) l=mid+1;
else r=mid-1;
}
return l;
}
void insert(int x,int z,int y,int k){
if (k) {
s[++cnt].x=find(x); s[cnt].y=z; s[cnt].k=1;
s[++cnt].x=find(x); s[cnt].y=y; s[cnt].k=-1;
}
else {s[++cnt].x=find(x); s[cnt].r=find(z); s[cnt].y=y;}
}
void add(int x,int y){
while (x<=n) {
t[x]+=y;
x+=low(x);
}
}
int ask(int x){
int ans=0;
while (x){
ans+=t[x];
x-=low(x);
}
return ans;
}
int main(){
n=read();
rep(i,1,n){
a[i].x=read(); a[i].y=read();
hash[i]=a[i].x;
}
sort(hash+1,hash+1+n);
sort(a+1,a+1+n,cmp);
rep(i,2,n) if (a[i].x==a[i-1].x) {
insert(a[i].x,a[i-1].y,a[i].y,1);
}
sort(a+1,a+1+n,cmp2);
rep(i,2,n) if (a[i].y==a[i-1].y){
insert(a[i-1].x,a[i].x,a[i].y,0);
}
sort(s+1,s+1+cnt,cmp3);
rep(i,1,cnt){
if (s[i].k==0) ans+=ask(s[i].r-1)-ask(s[i].x);
else add(s[i].x,s[i].k);
}
printf("%d\n",ans+n);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: