題目大意:題目給定一面牆(一維),在牆上貼海報,(li-ri)的區間形式張貼n張海報,求最終在牆上能看見多少張海報?
區間覆蓋的線段樹的求法是這樣的,每個區間給定一個標記,例如(1,10),(2,6)張貼兩張海報,那麼(1,10)區間的節點就標記為1,(2,6)區間的節點就標記為2,這樣就實現了區間覆蓋,標記完之後會發現(1,1)為標記1,(2,6)為標記2,(7,10)為標記1,這樣會得出三段間斷的區間,怎樣來將這三段區間判斷為2個呢?用一個hash數組,對當前節點的標記進行1,0操作。因為每個結點都是記錄的自己所在區間的標號,所以即便是這個區間被其他區間劃分為K段,也可以通過對標記的1,0操作判斷之前是否被標記過,若沒有標記過沒那麼可見的海報數直接++就行了。否則不+;
題中的海報牆可是非常大的..10^7這樣對於空間開銷很大。這裡運用到了座標離散化的思想,將自己需要的座標保留,不需要的剔除掉,這裡可以參見LRJ黑書的切蛋糕離散化問題,但是黑書的特點就是含混不清,難以理解= =,所以看完之後我一直不知道怎麼來編碼實現這個問題。他給的例子也用很嚴重的二義性!關於離散化,網路上也沒有很好的解釋,因為離散化只是一種思想,對於具體題目會有不同的運用,舉個例子吧,例如給定區間為(1,10000),(200,1000),很明顯的,這兩段區間有利用價值的就是1,200,1000,10000這四個點,直接將他們排序,對於每個下標就是他們的映射,0,1,2,3,這樣比較好理解,所以在數組中儲存的就是array[0]=1,arry[1]=200;這樣有什麼用呢?我們使用線段樹進行操作的時候只是對這種數組下標進行操作,因為不管原來給定的座標值有多大,直接映射過來為較小的數,保證相對順序不變就可以了,也就是性質不變。原來的1->0,200>1,1000->3,我們若要執行其他的操作的話,就在這種下標去尋找原來的值就可以了。在這個題目中,只需要統計海報的總數,所以,長度便沒有那麼重要了。怎樣去利用下標來構造樹呢?之前我們儲存了座標的值,通過二分去尋找座標在數組中的位置,再將這些位置去構造二叉線段樹。
對於這個題目我不懂的就是為啥,兩個區間端點相差>1時,必須多插一個值?? 覺得沒有必要啊... 希望hh神犇能給我回複吧....
做完這道題,真的感覺線段樹有了那麼一點點的想法了,真的要活學活用啊~ 感謝hh神犇。
#include<iostream>#include<algorithm>#define MAXN 11111using namespace std;int X[MAXN*3],lx[MAXN],rx[MAXN];int col[MAXN<<4];bool hash[MAXN<<2];int cnt;void PushDown( int rt ){ if( col[rt]!=-1 ) { col[rt<<1]=col[rt<<1|1]=col[rt]; col[rt]=-1; } return ;}int Bin( int key,int n ){ int left=0,right=n-1; while( left<=right ) { int m=(left+right)>>1; if( X[m]==key )return m; if( X[m]>key )right=m-1; else left=m+1; } return -1;}void update( int l,int r,int c,int L,int R,int rt ){ if( l<=L&&R<=r ){ col[rt]=c; return ; } PushDown(rt); int m=(L+R)>>1; if( l<=m ) update( l,r,c,L,m,rt<<1 ); if( r>m ) update( l,r,c,m+1,R,rt<<1|1 );}void query( int l,int r,int rt ){ if( col[rt]!=-1 ) { if( !hash[col[rt]] ) cnt++; hash[col[rt]]=true; return ; } if( l==r )return ; //PushDown(rt); int m=( l+r )>>1; query( l,m,rt<<1 ); query( m+1,r,rt<<1|1 );}int main(){ int T,n,nn,i; scanf( "%d",&T ); while( T-- ) { scanf( "%d",&n ); nn=0; for( i=0;i<n;i++ ) { scanf( "%d %d",&lx[i],&rx[i] ); X[nn++]=lx[i]; X[nn++]=rx[i]; } sort( X,X+nn ); int m=1; for( i=1;i<nn;i++ ){ if( X[i]!=X[i-1] ) X[m++]=X[i]; } for( i=m-1;i>=1;i-- ){ if( X[i]!=X[i-1]+1 ) X[m++]=X[i]+1; } sort( X,X+m ); memset( col,-1,sizeof(col) ); for( i=0;i<n;i++ ) { int l=Bin( lx[i],m ); int r=Bin( rx[i],m ); update( l,r,i,0,m,1 ); } cnt=0; memset( hash,false,sizeof(hash) ); query( 0,m,1 ); printf( "%d\n",cnt ); } return 0;}