POJ 2528 線段樹 離散化 逆向思維
阿新 • • 發佈:2019-01-07
要求:向長度為1e8的牆上貼最多1e4個長度不等的海報,每個海報貼在牆上任意位置,問最後能見到幾個海報(見到海報的一部分也算是見到該海報)。
方法:線段樹區間修改 離散化 逆向思維
1.建造一個1e8的線段樹必定TLE,因此需要離散化(壓縮區間)。
此題離散化具體步驟:先用ql陣列和qr陣列讀入每個海報的左右端點,並用newq陣列儲存海報的所有端點並升序排列,用unique函式去重,cnt=unique(newq,newq+cnt)-newq;返回去重後的端點個數,大小為1e8的ver陣列的下標為原端點,對應的數值為離散化後的端點。離散化時需特別注意:[1,10],[1,4],[5,10]和 [1,10],[1,4],[6,10]離散化後的端點一樣,均為[1,2],[3,4],[1,4],但結果分別為2和3。故離散化後的端點不能緊貼著,需留有1個間隔。
2.逆向思維是假如倒著順序貼海報,那麼如果貼的位置有一個空位,那麼就可以看到,ans++。
3.套區間修改模板。
#include<stdio.h> #include<math.h> #include<string.h> #include<algorithm> using namespace std; int k,q,a,b,c,flag; int ql[30005],qr[30005],color[1000005]; int newq[30005],ver[30000005],seg[30000005]; void init() { int i,j,k; scanf("%d",&q); memset(color,0,sizeof(color)); } //color[i]=0表示空 color[i]=-1表示未滿 color[i]=1表示滿 void update(int i,int l,int r) { int m=l+(r-l)/2; if(a<=l&&r<=b&&color[i]==0)//colorv 存的是區間+color { color[i]=1; flag=1; return; } if(color[i]==1) { color[i*2]=1; color[i*2+1]=1; } if(a<=m&&color[i]<=0) update(i*2,l,m); if(b>m&&color[i]<=0) update(i*2+1,m+1,r); if(color[i*2]+color[i*2+1]==1) color[i]=-1; if(color[i*2]+color[i*2+1]==2) color[i]=1; } int main() { int i,j,k,t,cnt,ans; scanf("%d",&t); for(i=1;i<=t;i++) { init(); cnt=0; for(j=1;j<=q;j++)//輸入海報的左右端點 { scanf("%d",&ql[j]); newq[cnt++]=ql[j]; scanf("%d",&qr[j]); newq[cnt++]=qr[j]; } sort(newq,newq+cnt); cnt=unique(newq,newq+cnt)-newq; //排序左右端點得到 for(j=0;j<cnt;j++) { ver[newq[j]]=j+2; } ans=0; for(j=q;j>=1;j--)//倒著貼 只要有空位就加1 { a=ver[ql[j]],b=ver[qr[j]],c=1; flag=0; update(1,1,100000); if(flag) ans++; } printf("%d\n",ans); } }