POJ 2528 經典!線段樹離散化
http://poj.org/problem?id=2528
題意:n(n<=10000)個人依次貼海報,給出每張海報所貼的範圍li,ri(1<=li<=ri<=10000000)。求出最後還能看見多少張海報。
解法:離散化,如下面的例子(題目的樣例),因為單位1是一個單位長度,將下面的
1 2 3 4 6 7 8 10
— — — — — — — —
1 2 3 4 5 6 7 8
離散化 X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10
於是將一個很大的區間對映到一個較小的區間之中了,然後再對每一張海報依次更新在寬度為1~8的牆上(用線段樹),最後統計不同顏色的段數。
但是隻是這樣簡單的離散化是錯誤的,
如三張海報為:1~10 1~4 6~10
離散化時 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一張海報時:牆的1~4被染為1;
第二張海報時:牆的1~2被染為2,3~4仍為1;
第三張海報時:牆的3~4被染為3,1~2仍為2。
最終,第一張海報就顯示被完全覆蓋了,於是輸出2,但實際上明顯不是這樣,正確輸出為3。
新的離散方法為:在相差大於1的數間加一個數,例如在上面1 4 6 10中間加5(
X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10
這樣之後,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3
最終,1~2為2,3為1,4~5為3,於是輸出正確結果3。
我是這樣理解這個離散化為啥要加中間一個點:如果貼海報只有兩個端點很容易出錯,但如果中間有一個點會和兩個端點有一樣的顏色,統計的時候就不會出錯(個人想法!!!)
#include <iostream> #include<algorithm> #include<stdio.h> #include<string.h> using namespace std; const int maxn=10100; #define lson node<<1,l,mid #define rson node<<1|1,mid+1,r int x[maxn<<4]; int tree[maxn<<4],ans=0,n=0,num=1; bool hash[maxn<<2]; int lx[maxn<<2],rx[maxn<<2]; void pushdown(int node) { tree[node<<1]=tree[node<<1|1]=tree[node]; tree[node]=-1; } void update(int node ,int l,int r,int begin, int end,int x) { if(begin<=l&&end>=r) { tree[node]=x; return; } if(tree[node]!=-1) pushdown(node); int mid=(l+r)>>1; if(mid>=begin) update(lson,begin,end,x); if(mid<end) update(rson,begin,end,x); } void query(int node ,int l,int r) { if(l==r) { if(hash[tree[node]]==0) { hash[tree[node]]=1; ans++; } tree[node]=-1; return; } if(tree[node]!= -1) pushdown(node); int mid=(l+r)>>1; query(lson); query(rson); } int bsearch(int ll, int rr,int xx) { int mm; while(ll<=rr) { mm=(ll+rr)>>1; if(x[mm]==xx) return mm; else if(x[mm]>xx) rr=mm-1; else ll=mm+1; } return ll; } int main() { int T; scanf("%d",&T); while(T--) { memset(tree,-1,sizeof(tree)); memset(hash,0,sizeof(hash)); int cnt=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&lx[i],&rx[i]); x[++cnt]=lx[i]; x[++cnt]=rx[i]; } sort(x+1,x+1+cnt); num=1; for(int i=2;i<=cnt;i++) { if(x[i]!=x[i-1]) x[++num]=x[i]; } for(int i=num;i>1;i--) { if(x[i]-x[i-1]>1) x[++num]=x[i]-1; } sort(x+1,x+1+num); for(int i=1;i<=n;i++) { int l=bsearch(1,num,lx[i]); int r=bsearch(1,num,rx[i]); update(1,1,num,l,r,i); } ans=0; query(1,1,num); printf("%d\n",ans); } return 0; }
個人感覺大佬離散化程式碼實現寫的特別棒 orz!!!
還有一個點 : 原來自己寫二分的時候條件 寫的是 while(l<r) 其實這樣會取不到右端點的!!!對二分還是不熟練