ZOJ 1610Count the Colors 线段树区间染色问题

来源:互联网 发布:php笔试题及答案 编辑:程序博客网 时间:2024/04/27 21:02

题目链接: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 longusing 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;}


0 0
原创粉丝点击