1. 程式人生 > >POJ 2528 線段樹 離散化 逆向思維

POJ 2528 線段樹 離散化 逆向思維

要求:向長度為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);
	}
}