【BZOJ1818】[CQOI2010]內部白點(樹狀陣列,掃描線)
阿新 • • 發佈:2018-11-11
【BZOJ1818】[CQOI2010]內部白點(樹狀陣列,掃描線)
題面
題解
不難發現\(-1\)就是在搞笑的。
那麼對於每一行,我們顯然可以處理出來最左和最右的點,那麼等價於我們在橫著的方向上得到了若干條線段,同理,在豎直方向上也得到了若干條線段,那麼最終的答案就是這些線段的交點個數加上原先就有的白點個數,再減去交點上的黑點數目。
直接離散沒有任何問題,那麼直接掃描線計算即可。
程式碼有點醜。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define MAX 100100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct Node{int x,y;}p[MAX]; bool operator<(Node a,Node b){if(a.y^b.y)return a.y<b.y;return a.x<b.x;} bool cmp(Node a,Node b){if(a.x^b.x)return a.x<b.x;return a.y<b.y;} struct Line{int x,y,v;}X[MAX<<1]; bool operator<(Line a,Line b){if(a.x^b.x)return a.x<b.x;return a.y<b.y;} int Sx[MAX],tx,Sy[MAX],ty; int n,ans,tot,c[MAX],v[MAX]; int lb(int x){return x&(-x);} void add(int x,int w){while(x<=n)c[x]+=w,x+=lb(x);} int getsum(int x){int s=0;while(x)s+=c[x],x-=lb(x);return s;} int main() { n=read();ans=n; for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(); for(int i=1;i<=n;++i)Sx[++tx]=p[i].x,Sy[++ty]=p[i].y; sort(&Sx[1],&Sx[tx+1]);tx=unique(&Sx[1],&Sx[tx+1])-Sx-1; sort(&Sy[1],&Sy[ty+1]);ty=unique(&Sy[1],&Sy[ty+1])-Sy-1; for(int i=1;i<=n;++i)p[i].x=lower_bound(&Sx[1],&Sx[tx+1],p[i].x)-Sx; for(int i=1;i<=n;++i)p[i].y=lower_bound(&Sy[1],&Sy[ty+1],p[i].y)-Sy; sort(&p[1],&p[n+1]); for(int i=1,j;i<=n;i=j+1) { j=i;while(j<n&&p[i].y==p[j+1].y)++j; X[++tot]=(Line){p[i].x,p[j].y,1}; X[++tot]=(Line){p[j].x+1,p[j].y,-1}; } sort(&p[1],&p[n+1],cmp);sort(&X[1],&X[tot+1]); for(int i=1,j,pos=1;i<=n;i=j+1) { j=i;while(j<n&&p[i].x==p[j+1].x)++j; int l=p[i].y,r=p[j].y,x=p[i].x; while(pos<=tot&&X[pos].x<=x) { int xx=X[pos].y;v[xx]+=X[pos].v; if(!v[xx])add(X[pos].y,-1); else if(v[xx]==1)add(X[pos].y,1); ++pos; } ans+=getsum(r)-getsum(l-1); for(int k=i;k<=j;++k)if(v[p[k].y])ans-=1; } printf("%d\n",ans); return 0; }