ZOJ 1610 Count the Colors 線段樹區間染色問題

題目鏈接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66989#problem/F

題意:給一個區間範圍,然後逐步給某些區間染色,最後問能各種能看見的顏色的塊數。典型的線段樹染色問題,看了別人的代碼不太能理解。但是想到了一個不錯的思路。

解題思路:我們可以這樣想,顯然每條線段最後的顏色都由改線段最後一次的染色決定,如果我們把每一次染色都標記一個步數,顯然每條線段的最後顏色就由其上面的區間中所包含的最大步數所決定,這樣就好想多了,我們每一次染色就對目標區間插入一個步數,順便記錄下該步數所用的顏色,最後用O(n)的時間求出每個葉子的最後步數和顏色即可。
由於線段樹最後的葉子一般都是一個點,而本題最小的單位是一條線段而不是一個點,所以我們把每一條線段看成一個點,建立線段樹。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 8010
#define LL long long

using namespace std;
struct node{
    int l,r,w;//區間的左右端點,和該區間的最後一個染色步數
}T[maxn*3];

int n,col[maxn],A[maxn],cnt[maxn];

void build(int L,int R,int o){
    T[o].l=L;T[o].r=R;T[o].w=0;
    if(L==R) return;
    build(L,(L+R)/2,o*2);
    build((L+R)/2+1,R,o*2+1);
}

void add(int L,int R,int step,int o){
    if(T[o].l>=L && T[o].r<=R) {T[o].w=step;return;}
    if(T[o].l==T[o].r) return;
    if(L>=T[o*2+1].l) add(L,R,step,o*2+1);
    else if(R<=T[o*2].r) add(L,R,step,o*2);
    else{
        add(L,T[o*2].r,step,o*2);
        add(T[o*2+1].l,R,step,o*2+1);
    }
}

void dfs(int o,int Max){//dfs遍歷線段樹並求出葉子的顏色
    Max=max(Max,T[o].w);
    if(T[o].l==T[o].r) {A[T[o].l]=col[Max];return;}
    dfs(o*2,Max);
    dfs(o*2+1,Max);
}

void disp(){
    int t=-1;
    memset(cnt,0,sizeof(cnt));
    for(int i=0;i<=8000;i++){//處理相連顏色的情況
        if(A[i]==-1 || A[i]==t){t=A[i];continue;}
        t=A[i];
        cnt[t]++;
    }
    for(int i=0;i<=8000;i++) if(cnt[i])
        printf("%d %d\n",i,cnt[i]);
    printf("\n");
}

int main(){
    //freopen("in.txt","r",stdin);
    while(cin>>n){
        build(1,8000,1);//把每一線段看成一個端點,所以從1開始
        int a,b,c;
        col[0]=-1;
        for(int i=1;i<=n;i++){
            scanf("%d %d %d",&a,&b,&c);
            col[i]=c;//記錄第i個輸入的顏色
            add(a+1,b,i,1);//這裏以單位線段爲一個點,插入一個步數
        }
        memset(A,-1,sizeof(A));//-1表色沒有染過色
        dfs(1,0);//更新每個葉子的最後步數和顏色
        disp();//輸出答案
    }
    return 0;
}


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