1. 程式人生 > >bzoj1818 內部白點(好題) 離散化+樹狀陣列

bzoj1818 內部白點(好題) 離散化+樹狀陣列

題目傳送門

  題意:給出很多黑點,當一個座標上下左右都有黑點時,這個點也被染成黑色,問最後黑點的數量。

  思路:首先,一個很顯然的結論,不可能出現無限染色的情況。所以不會輸出-1,當n為0或者1時,答案就是0或者1.

    其次,每一個新增的點其實就是橫線和豎線的交點,我們先把所有的座標都離散化,然後把橫線和豎線都處理出來,分三類,橫線,豎線的下端點,豎線的上端點,按照y從小到大排序。遇到豎線下端點時,樹狀陣列x的位子加一,遇到上端點,x的位置減一,遇到橫線,則是一段區間求和。

    比較重要的端點問題和處理這三類點(線)的優先順序問題,顯然應該先刪去,再求和,最後加上,所以在結構體中加入一個flag,既表示種類又表示優先順序。區間求和的時候端點要注意。

    我一開始將一條線上的所有點全部合併在同一條直線上,比如(1,1),(1,2),(1,3)直接合併成(1,1)到(1,3),這樣處理首先很麻煩,其次會遇到某兩條直線把(1,2)這個點染成黑色,重複計算的問題,所以只需要老老實實的分成很多線段就可以了。

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=100010;
struct node{
    ll x,y;
}a[maxn];
struct line{ ll x1,y1,x2,y2; int flag;//1下端點 2橫線 3代表上端點 }b[maxn<<2]; bool cmpx(const node &u,const node &v) { if(u.x==v.x)return u.y<v.y; return u.x<v.x; } bool cmpy(const node &u,const node &v) { if(u.y==v.y)return u.x<v.x; return u.y<v.y; }
bool cmpli(const line &u,const line &v){ if(u.y1==v.y1)return u.flag>v.flag; return u.y1<v.y1; } ll hash1[maxn<<1],hash2[maxn<<2]; int n,h; inline void Hash(){ sort(hash1+1,hash1+1+h); int m=0; for(int i=1;i<=h;i++) { if(i==1||hash1[i]!=hash1[i-1]){ hash2[++m]=hash1[i]; } } for(int i=1;i<=n;i++) { a[i].x=lower_bound(hash2+1,hash2+1+m,a[i].x)-hash2; a[i].y=lower_bound(hash2+1,hash2+1+m,a[i].y)-hash2; } } ll c[maxn<<1]; inline void add(int x,ll val) { while(x<=200000) { c[x]+=val; x+=(x&(-x)); } } inline ll getsum(int x) { ll res=0; while(x>0) { res+=c[x]; x-=(x&(-x)); } return res; } int main(){ cin>>n; for(int i=1;i<=n;i++) { scanf("%lld%lld",&a[i].x,&a[i].y); hash1[++h]=a[i].x,hash1[++h]=a[i].y; } if(n<=1){ printf("%d\n",n); return 0; } Hash(); sort(a+1,a+1+n,cmpx); ll xx=a[1].x,yy=a[1].y; int totline=1; for(int i=2;i<=n;i++)//豎線 { if(a[i].x==xx) { b[totline].x1=xx; b[totline].y1=yy; b[totline].x2=a[i].x; b[totline].y2=a[i].y; xx=a[i].x; yy=a[i].y; totline++; }else{ xx=a[i].x; yy=a[i].y; } if(i==n){ totline--; } } sort(a+1,a+1+n,cmpy); for(int i=1;i<=totline;i++) { b[i].flag=1; b[i+totline]=b[i]; b[i+totline].flag=3; b[i].x2=b[i].x1,b[i].y2=b[i].y1; b[i+totline].x1=b[i+totline ].x2,b[i+totline].y1=b[i+totline ].y2; } totline<<=1; totline++; xx=a[1].x,yy=a[1].y; for(int i=2;i<=n;i++)//豎線 { if(a[i].y==yy) { b[totline].x1=xx; b[totline].y1=yy; b[totline].x2=a[i].x; b[totline].y2=a[i].y; b[totline].flag=2; xx=a[i].x; yy=a[i].y; totline++; }else{ xx=a[i].x; yy=a[i].y; } if(i==n)totline--; } sort(b+1,b+1+totline,cmpli); //1下端點 2橫線 3代表上端點 // for(int i=1;i<=totline;i++) // { // printf("i:%d flag:%d x1:%d y1:%d x2:%d y2:%d\n",i,b[i].flag,b[i].x1,b[i].y1,b[i].x2,b[i].y2); // } ll ans=0; for(int i=1;i<=totline;i++) { if(b[i].flag==1){ add(b[i].x1,1); }else if(b[i].flag==2){ xx=b[i].x1,yy=b[i].x2; if(yy-1>=xx+1){ ans-=getsum(xx); ans+=getsum(yy-1); } }else if(b[i].flag==3){ add(b[i].x1,-1); } } printf("%lld\n",ans+n); } /* 10 6 7 3 8 5 2 6 4 1 5 5 4 1 10 8 7 1 8 4 10 9 2 4 4 1 8 4 7 1 7 10 1 10 6 4 6 8 6 1 */
View Code

1818: [Cqoi2010]內部白點

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1418  Solved: 635
[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

Sample Output

5

資料範圍
36%的資料滿足:n < = 500
64%的資料滿足:n < = 30000
100%的資料滿足:n < = 100000