1. 程式人生 > >POJ2528【線段樹經典染色。】

POJ2528【線段樹經典染色。】

來一點轉的線段樹精髓:

1、 線段樹是二叉樹,且必定是平衡二叉樹,但不一定是完全二叉樹。

2、 對於區間[a,b],令mid=(a+b)/2,則其左子樹為[a,mid],右子樹為[mid+1,b],當a==b時,該區間為線段樹的葉子,無需繼續往下劃分。

3、 線段樹雖然不是完全二叉樹,但是可以用完全二叉樹的方式去構造並存儲它,只是最後一層可能存在某些葉子與葉子之間出現“空葉子”,這個無需理會,同樣給空葉子按順序編號,在遍歷線段樹時當判斷到a==b時就認為到了葉子,“空葉子”永遠也不會遍歷到。

4、 之所以要用完全二叉樹的方式去儲存線段樹,是為了提高在插入線段和搜尋時的效率。用p*2,p*2+1的索引方式檢索p的左右子樹要比指標快得多。

5、線段樹的精髓是,能不往下搜尋,就不要往下搜尋,儘可能利用子樹的根的資訊去獲取整棵子樹的資訊。如果在插入線段或檢索特徵值時,每次都非要搜尋到葉子,還不如直接建一棵普通樹更來得方便。

主動思考,一開始用簡單hash判斷染色,可自己總是扯不上線段樹去。

後來明白了思路,每次判斷染色時便把左右區間修改為i(即第幾次修改..)   【顯然這樣才能利用線段樹的優勢啊】

可離散化又出了點問題,學到一招用二分離散化,自己以前學到的那個離散化就是排好序後分別賦值i++,可距離大於1的時候+1就不好處理了,

順便看ACRush以及別人的各種回憶錄,以及在暑假做的某些題,二分或者二分的思路真的應用非常非常廣,OK!

在我看來線段最精髓的地方就是pushdown,只有在查詢或Update有交集的時候才會更新。。

還有一個很重要的地方就是陣列開的大小,為確保自己在記憶體允許的情況儘量開大。

自己分析的時候難免有錯。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r

const int maxn=11111;

int col[maxn*16];//這地方必須乘的係數得萬分注意
int li[maxn<<2];
int ri[maxn<<2];
bool hash[maxn<<2];//..
int ans=0;
int x[maxn<<2];
int y[maxn<<2];

void Pushdown(int rt)
{
    if(col[rt]!=-1)
    {
      col[rt<<1]=col[rt<<1|1]=col[rt];
      col[rt]=-1;
    }
}
void update(int ql,int qr,int ch,int rt,int l,int r)
{
    if(ql<=l&&qr>=r)
    {
        col[rt]=ch;
        return;
    }

    Pushdown(rt);
    int m=(l+r)>>1;
    if(ql<=m)
        update(ql,qr,ch,lson);
    if(qr>m)
        update(ql,qr,ch,rson);
}
void query(int ql,int qr,int rt,int l,int r)
{
    if(col[rt]!=-1)
    {
        if(hash[col[rt]]==false)//曾錯在這地方 一種顏色的統計一次就夠了。
        {
             ans++;
             hash[col[rt]]=true;
        }
        col[rt]=-1;
        return;
    }

    if(l==r) return;//要這一步其實是必須的 特殊情況是考慮分叉

    Pushdown(rt);//這一步應該有或沒有都能AC。

    int m=(l+r)>>1;
    if(ql<=m)
     query(ql,qr,lson);
    if(qr>m)
     query(ql,qr,rson);
}
int Bin(int ans,int x[],int l,int r)
{
    while(l<r)
    {
      int m=(l+r)>>1;
      if(x[m]==ans)
        return m;
      if(ans<=x[m])
        r=m-1;
      else l=m+1;
    }
    return l;
}

int main()
{
    int case_num;
    scanf("%d",&case_num);
    while(case_num--)
    {
        ans=0;
        memset(col,-1,sizeof(col));
        memset(hash,false,sizeof(hash));
        int n;
        scanf("%d",&n);
        int cnt=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&li[i],&ri[i]);
            x[cnt++]=li[i];
            x[cnt++]=ri[i];
        }

        //離散化。 應該又學到一招  不一定靠id 使用二分
        sort(x+1,x+cnt);
        int mm=cnt;
        for(int i=2;i<cnt;i++)
        {
            if((x[i]-x[i-1])>1)
              x[mm++]=x[i-1]++;
        }
        sort(x+1,x+mm);

        int tt=2;
        y[1]=x[1];
        for(int i=2;i<mm;i++)
        {
            if(x[i]!=x[i-1])
              y[tt++]=x[i];
        }

        for(int i=1;i<=n;i++)
        {
            int l=Bin(li[i],y,1,tt-1);
            int r=Bin(ri[i],y,1,tt-1);
            update(l,r,i,1,1,tt-1);
        }
        query(1,tt-1,1,1,tt-1);
        printf("%d\n",ans);
    }
    return 0;
}