POJ 2777(线段树+状态压缩)

传送门

题面:

Count Color

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 51631   Accepted: 15566

Description

Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. 

There is a very long board with length L centimeter, L is a positive integer, so we can evenly divide the board into L segments, and they are labeled by 1, 2, ... L from left to right, each is 1 centimeter long. Now we have to color the board - one segment with only one color. We can do following two operations on the board: 

1. "C A B C" Color the board from segment A to segment B with color C. 
2. "P A B" Output the number of different colors painted between segment A and segment B (including). 

In our daily life, we have very few words to describe a color (red, green, blue, yellow…), so you may assume that the total number of different colors T is very small. To make it simple, we express the names of colors as color 1, color 2, ... color T. At the beginning, the board was painted in color 1. Now the rest of problem is left to your. 

Input

First line of input contains L (1 <= L <= 100000), T (1 <= T <= 30) and O (1 <= O <= 100000). Here O denotes the number of operations. Following O lines, each contains "C A B C" or "P A B" (here A, B, C are integers, and A may be larger than B) as an operation defined previously.

Output

Ouput results of the output operation in order, each line contains a number.

Sample Input

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

Sample Output

2
1

题意:

    一条很长(L)的画板,有T种颜色,O个操作;每次操作将一个区间刷成一种颜色,或者查询一个区间内所含的颜色数。

题目分析:

    经典的区间染色问题。

    因为总共的颜色最多只有30种,因此我们可以用一个范围在int的二进制数bit存储每一种颜色(bit的二进制下的每一位代表着一种颜色)

    之后我们只需要用线段树对区间上的bit进行维护即可。注意在我们区间合并push_up的过程中,我们需要将某个结点的左右儿子的值都或起来,作为该结点的值。

    之后对于操作C,我们只需要将区间[l,r]的值更新即可。

    对于操作Q,我们只需要将区间[l,r]的bit求出,并求出bit在二进制位下的1的个数为答案。

    ps:这个问题种还有一个坑点,对于每一个操作种的l和r,题目中并没有说明哪个是左区间,哪个是有区间,因此我们还需要特判一下大小。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#define maxn 100005
using namespace std;
int a[maxn],sum[maxn<<2],add[maxn<<2];
void push_up(int rt){
    sum[rt]=sum[rt<<1]|sum[rt<<1|1];
}
void push_down(int rt)
{
    if(add[rt]){
        add[rt<<1]=add[rt<<1|1]=add[rt] ;
        sum[rt<<1]=sum[rt<<1|1]=add[rt] ;
        add[rt]=0 ;
    }
}
void build(int l,int r,int rt){
    sum[rt]=add[rt]=0;
    if(l==r){
        sum[rt]=1;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    push_up(rt);
}
void update(int L,int R,int C,int l,int r,int rt){
    if(L>r||R<l) return;
    if(L<=l&&R>=r){
        add[rt]=1<<(C-1);
        sum[rt]=1<<(C-1);
        return;
    }
	push_down(rt);
    int m=(l+r)>>1;
    update(L,R,C,l,m,rt<<1);
    update(L,R,C,m+1,r,rt<<1|1);
    push_up(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(L>r||R<l) return 0;
    if(L<=l&&R>=r){
        return sum[rt];
    }
    int m=(l+r)>>1;
    push_down(rt);
    return query(L,R,l,m,rt<<1)|query(L,R,m+1,r,rt<<1|1);
}
int main()
{
    int L,T,O;
    scanf("%d%d%d",&L,&T,&O);
    build(1,L,1);
    for(int i=0;i<O;i++){
        char str[10];
        scanf("%s",str);
        int l,r;
        scanf("%d%d",&l,&r);
        if(l>r) swap(l,r);//坑点题目没说哪个大哪个小,需要特判
        if(str[0]=='C'){
            int C;
            scanf("%d",&C);
            update(l,r,C,1,L,1);
        }
        else{
            int ans=query(l,r,1,L,1);
            int cnt=0;
            cnt=__builtin_popcount(ans);//获取某个数二进制位1的个数
            printf("%d
",cnt);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Chen-Jr/p/11007213.html