BZOJ 1818: [Cqoi2010]内部白点
题目: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; }