1. 程式人生 > >POJ 2528 經典!線段樹離散化

POJ 2528 經典!線段樹離散化

http://poj.org/problem?id=2528

題意:n(n<=10000)個人依次貼海報,給出每張海報所貼的範圍li,ri(1<=li<=ri<=10000000)。求出最後還能看見多少張海報。

解法:離散化,如下面的例子(題目的樣例),因為單位1是一個單位長度,將下面的

      1   2   3   4  6   7   8   10

     —  —  —  —  —  —  —  —

      1   2   3   4  5   6   7   8

離散化  X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10

於是將一個很大的區間對映到一個較小的區間之中了,然後再對每一張海報依次更新在寬度為1~8的牆上(用線段樹),最後統計不同顏色的段數。

但是隻是這樣簡單的離散化是錯誤的,

如三張海報為:1~10 1~4 6~10

離散化時 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一張海報時:牆的1~4被染為1;
第二張海報時:牆的1~2被染為2,3~4仍為1;
第三張海報時:牆的3~4被染為3,1~2仍為2。
最終,第一張海報就顯示被完全覆蓋了,於是輸出2,但實際上明顯不是這樣,正確輸出為3。

新的離散方法為:在相差大於1的數間加一個數,例如在上面1 4 6 10中間加5(

演算法中實際上1,4之間,6,10之間都新增了數的)

X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10

這樣之後,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3

最終,1~2為2,3為1,4~5為3,於是輸出正確結果3。

我是這樣理解這個離散化為啥要加中間一個點:如果貼海報只有兩個端點很容易出錯,但如果中間有一個點會和兩個端點有一樣的顏色,統計的時候就不會出錯(個人想法!!!)

#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=10100;

#define lson node<<1,l,mid
#define rson node<<1|1,mid+1,r
int x[maxn<<4];
int tree[maxn<<4],ans=0,n=0,num=1;
bool hash[maxn<<2];
int lx[maxn<<2],rx[maxn<<2];

void pushdown(int node)
{
    tree[node<<1]=tree[node<<1|1]=tree[node];
    tree[node]=-1;
}

void update(int node ,int l,int r,int begin, int end,int x)
{
    if(begin<=l&&end>=r)
    {
          tree[node]=x; return;
    }
    if(tree[node]!=-1) pushdown(node);

    int mid=(l+r)>>1;
    if(mid>=begin) update(lson,begin,end,x);
    if(mid<end) update(rson,begin,end,x);
}

void query(int node ,int l,int r)
{
    if(l==r)
    {
       if(hash[tree[node]]==0)
       {
           hash[tree[node]]=1;  ans++;
       }
       tree[node]=-1;
       return;
    }

    if(tree[node]!= -1) pushdown(node);
    int mid=(l+r)>>1;
    query(lson); query(rson);
}

int bsearch(int ll, int rr,int xx)
{
    int mm;
    while(ll<=rr)
    {
        mm=(ll+rr)>>1;
        if(x[mm]==xx) return mm;
        else if(x[mm]>xx) rr=mm-1;
        else ll=mm+1;
    }
    return ll;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(tree,-1,sizeof(tree));
        memset(hash,0,sizeof(hash));
        int cnt=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&lx[i],&rx[i]);
            x[++cnt]=lx[i];
            x[++cnt]=rx[i];
        }

        sort(x+1,x+1+cnt);
        num=1;
        for(int i=2;i<=cnt;i++)
        {
            if(x[i]!=x[i-1]) x[++num]=x[i];
        }
        for(int i=num;i>1;i--)
        {
           if(x[i]-x[i-1]>1) x[++num]=x[i]-1;
        }
        sort(x+1,x+1+num);

        for(int i=1;i<=n;i++)
        {
            int l=bsearch(1,num,lx[i]);
            int r=bsearch(1,num,rx[i]);
            update(1,1,num,l,r,i);
        }

        ans=0;
        query(1,1,num);
        printf("%d\n",ans);
    }
    return 0;
}

個人感覺大佬離散化程式碼實現寫的特別棒 orz!!!

還有一個點 : 原來自己寫二分的時候條件 寫的是 while(l<r) 其實這樣會取不到右端點的!!!對二分還是不熟練