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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值