[POJ2777] 统计颜色 - 线段树


题目描述

有一个长度为L厘米板,L是一个正整数,所以我们可以把它均匀地划分成L个部分,分别从左到右编号为1,2……L,每一个部分长度都为1厘米。现在我们必须给每个部分涂色,一个部分一种颜色,要求完成以下两种操作:
  1.“C A B C1”:表示从A部分到B部分涂上C1颜色。
  2.“P A B”:表示从A部分到B部分涂了几种颜色。
  在我们的日常生活中,我们有非常少几种颜色(红色,绿色,蓝色,黄色…),所以你可以假设不同颜色的总数T是非常少。简单地说,我们表示颜色的名称为颜色1,颜色2,…..颜色T。最初时候,这个厘米版都涂成颜色1。


输入格式

输入文件的第一行包含三个整数L(1<=L<=10^5),T(1<=T<=30)和M(1<=M<=10^5)。M表示操作的次数。
以下M行,每行包含“C A B C1”或者“P A B”(A,B,C1都是整数,A可以比B大)。


输出格式

输出文件依顺序输出,每行一个数字。


样例数据

样例输入

2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2

样例输出

2
1


题目分析

线段树,维护当前线段的颜色
懒标记一下,找到就退。
将颜色状压塞进线段树里面,可以节约时间。


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
    int num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
const int maxn=100000;
struct Tree {
    int left,right,color,sum;
};
struct Segment_Tree {
    Tree tree[maxn*4];
    int ans;
    void build(int index,int Left,int Right) {
        tree[index].left=Left;
        tree[index].right=Right;
        tree[index].color=tree[index].sum=1;
        if(Left==Right)return;
        int mid=(Left+Right)/2;
        build(2*index,Left,mid);
        build(2*index+1,mid+1,Right);
    }
    void push_down(int index) { //标记下传,下传当前lazy
        if(!tree[index].color)return;
        tree[index*2].sum=tree[index*2+1].sum=tree[index].sum;
        tree[index*2].color=tree[index*2+1].color=tree[index].color;
        tree[index].color=0;
    }
    void push_up(int index) { //标记上传,合并子树信息
        tree[index].sum=tree[index*2].sum|tree[index*2+1].sum;
    }
    void modify(int index,int Left,int Right,int data) {
        if(Right<tree[index].left||Left>tree[index].right)return; //不相交
        if(Left<=tree[index].left&&Right>=tree[index].right) { //完全包含
            tree[index].color=tree[index].sum=data;
            return;
        }
        push_down(index); //标记下传
        modify(index*2,Left,Right,data);
        modify(index*2+1,Left,Right,data);
        push_up(index); //标记上传
    }
    void init() {
        ans=0;
    }
    void Query(int index,int Left,int Right) {
        if(Right<tree[index].left||Left>tree[index].right)return; //不相交
        if(Left<=tree[index].left&&Right>=tree[index].right) { //完全包含
            ans|=tree[index].sum;
            return;
        }
        push_down(index); //标记下传
        Query(index*2,Left,Right);
        Query(index*2+1,Left,Right);
        push_up(index); //标记上传
    }
};
Segment_Tree st;
int n,cn,m;
int main() {
    scanf("%d%d%d",&n,&cn,&m);
    st.build(1,1,n);
    for(int i=1; i<=m; i++) {
        char order;
        getchar();
        scanf("%c",&order);
        if(order=='C') {
            int Left,Right,color;
            scanf("%d%d%d",&Left,&Right,&color);
            if(Left>Right)swap(Left,Right);
            st.modify(1,Left,Right,1<<(color-1));
        } else {
            int Left,Right;
            scanf("%d%d",&Left,&Right);
            if(Left>Right)swap(Left,Right);
            st.init();
            st.Query(1,Left,Right);
            int cnt=0,tmp=st.ans;
            while(tmp) {
                if(tmp&1)cnt++;
                tmp>>=1;
            }
            printf("%d\n",cnt);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值