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,000,L的取值可達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]時,標記已貼海報爲po,return;
3.若該區間已貼海報,則pushdown給2個兒子,標記當前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;
}