poj 2528

poj2528 Mayor'sposters


題目鏈接:http://poj.org/problem?id=2528


題目大意:

在一張牆上張貼海報。每張海報有一個張貼區間。後面放的海報與前面放的海報的關係是:覆蓋,不相交,相交。給定海報數目與這些海報分別張貼的區間,問:所有的海報張貼完畢後,可以從牆上看見幾張海報。


思路:

乍一看,我們可以使用一個長數組記錄海報的信息。比如第一張的區間爲[a,b],那麼可以將數組的位置[a,b]設爲1。若第二張的區間爲[c,d],那麼將[c,d]設爲2...以此類推,在程序的最後,我們只需統計長數組中出現的不同數字的個數即可。算法的複雜度爲O(nL+ L) O(nL),由於n的取值可達10,000L的取值可達10,000,000。數組開不了這麼大,時間限制又只有1000ms,這是不可能完成的任務。

再一看,採用線段樹離散化處理可以很好地解決這個問題。由於座標的取值可達10,000,000,然而n的取值僅可達10,000。說明分佈在區間中的海報是稀疏的。使用離散化可以將座標的範圍極大地縮小,所開數組的長度至多爲O(2n),內存限制沒有問題!使用線段樹對區間進行操作,複雜度爲O(nlog2n)O(nlogn),時間限制沒有問題!接下來要解決的問題主要是:

區間離散化

更新區間

讀取海報標記的問題:

Tips

我的線段樹採用一個dat[]數組存儲,dat[i]數組存儲的是某節點對應的海報序號。

區間離散化:

離散化是個很好的技術,可以把稀疏分佈在大範圍內的區間壓縮到小範圍內,且不影響區間之間的覆蓋關係。我也是在網上找了許多博客,才瞭解這個技術的。(上課沒來哈...)

基本的思想:

對於n個海報對應的n個區間來說,定義一個結構體line存儲2n個端點,該結構體中存儲2個值:val(端點值)num(屬於第num個海報)。最後對line數組進行升序排序、去重。然後,沒錯!每一個端點對應的序號就是它們在數組中的順序。將原來每個區間中的2個端點的值替換成它們對應的序號,這樣一來,我們就可以構造出新的n個區間了。而且長數組的範圍被壓縮到了(0,2n]


更新區間

Tips:dat[i] = 0表示,節點i的區間沒貼海報,或不只一張海報。dat[i]> 0表示,節點i的區間張貼了海報dat[i]


void update ( fr , to , L , R , k , po)

使用update函數更新線段樹:


            1.當張貼區間[fr,to]與當前節點k的區間[L,R]不相交時,return;

            2.[fr,to]恰好覆蓋節點k的區間[L,R]時,標記已貼海報爲poreturn;

            3.若該區間已貼海報,則pushdown2個兒子,標記當前dat[k]= 0, 遞歸給2個兒子


讀取海報標記的問題:

voidsearch (int k)

從樹根往下遞歸,用ans記錄節點張貼海報序號不重複 的個數。輸出答案。


算法步驟:

區間離散化,包括排序

初始化線段樹

更新區間

讀取可見海報張數


算法複雜度:

O(n + 2nlog2n + 2n) ,即O(nlogn)

O(n)

O(nlogL) O(nlog2n),即O(nlogn)

O(n)

總的複雜度爲:O(nlogn)


源程序:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using std::sort;

int n,ans;
const int maxn = 20001;
int POST[maxn][2];
bool vis[maxn];

struct line {
	int val;
	int num;
}Line[maxn];

int dat[4*maxn];

bool cmp(const line & a , const line & b) {
	return a.val < b.val;
}

void init(int cnt) {
	int t = cnt;
	int k = 1;
	while( k < t)
		k *= 2;
	for(int i = 1 ; i <= 2*k - 1 ; i ++)
		dat[i] = 0;
}

void update(int fr , int to , int L , int R , int k , int po) {
	if(to < L || fr > R)
		return;
	else if(fr <= L && to >= R)
		dat[k] = po;
	else
	{
		if(dat[k] > 0)
		{
			dat[2*k] = dat[2*k+1] = dat[k];
			dat[k] = 0;
		}
		int mid = (L+R)>>1;
		update(fr,to,L,mid,2*k,po);
		update(fr,to,mid+1,R,2*k+1,po);
	}
}

void search(int k) {
	if(dat[k])
	{
        if(!vis[dat[k]])
		{
            vis[dat[k]] = true;
            ans++;
        }
        return ;
      }
      search(2*k);
      search(2*k+1);
}

int main() {
	int C;
	scanf("%d",&C);
	while(C--)
	{
		// 輸入並離散化 
		scanf("%d",&n);
		for(int i = 0 ; i < n ; i ++)
		{
			scanf("%d%d",&POST[i][0],&POST[i][1]);
			Line[2*i].num = -(i+1);
			Line[2*i].val = POST[i][0];
			Line[2*i+1].num = i+1;
			Line[2*i+1].val = POST[i][1];
		}
		sort(Line,Line+2*n,cmp);
		
		int tmp = Line[0].val, cnt = 1;
		for(int i = 0 ; i < 2*n ; i ++)
		{
			if(Line[i].val != tmp)
			{
				tmp = Line[i].val;
				cnt ++;
			}
			if(Line[i].num < 0)
				POST[-Line[i].num-1][0] = cnt;
			else
				POST[Line[i].num-1][1] = cnt;
		}
		
		init(cnt);
		for(int i = 0 ; i < n ; i ++)
			update(POST[i][0],POST[i][1],1,cnt,1,i+1);
			
		memset(vis,0,sizeof(vis));
		ans = 0;
		
		search(1);
		printf("%d\n",ans);
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章