POJ - 2777 Count Color(线段树 + 状压)

14 篇文章 0 订阅
3 篇文章 0 订阅

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

区间染色问题,这里的颜色数量非常少,只有30种,因此可以用线段树+状压来维护

用一个数的二进制每一位来表示有没有这种颜色,利用“或”预算就能轻易的合并状态,最后用“与”运算按位取出。

30种颜色,需要一个2^30的数,刚刚好能用int存下。

// #include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;

const int maxn = 100005;

int sum[maxn << 2], lazy[maxn << 2];

//这是用来debug的,输出二进制
void print_bin(int n) {
    int l = sizeof(n) * 8; //总位数。
    int i;
    if(i == 0) {
        printf("0");
        return;
    }
    for(i = l - 1; i >= 0; i --) { //略去高位0.
        if(n & (1 << i)) break;
    }

    for(; i >= 0; i --)
        printf("%d", (n & (1 << i)) != 0);
}

void pushUp(int i) {
    // 或运算合并状态
    sum[i] = sum[i << 1] | sum[i << 1 | 1];
}

void pushDown(int i) {
    if(!lazy[i]) {
        return;
    }
    sum[i << 1] = sum[i];
    sum[i << 1 | 1] = sum[i];
    lazy[i << 1] = 1;
    lazy[i << 1 | 1] = 1;
    lazy[i] = 0;
}


void build(int i, int l, int r) {
    sum[i] = 1;
    if(l == r) {
        return;
    }
    int mid = (l + r) / 2;
    build(i << 1, l, mid);
    build(i << 1 | 1, mid + 1, r);
}

void update(int i, int L, int R, int l, int r, int cor) {
    if(l >= L && r <= R) {
        sum[i] = 1 << (cor - 1);
        lazy[i] = 1;
        return;
    }
    pushDown(i);
    int mid = (l + r) / 2;
    if(L <= mid) {
        update(i << 1, L, R, l, mid, cor);
    }
    if(R > mid) {
        update(i << 1 | 1, L, R, mid + 1, r, cor);
    }
    pushUp(i);
}

int query(int i, int L, int R, int l, int r) {
    if(l >= L && r <= R) {
        return sum[i];
    }
    pushDown(i);
    int ans = 0;
    int mid = (l + r) / 2;
    if(L <= mid) {
        ans |= query(i << 1, L, R, l, mid);
    }
    if(R > mid) {
        ans |= query(i << 1 | 1, L, R, mid + 1, r);
    }
    return ans;
}



int main() {
    int n, t, m;
    scanf("%d%d%d", &n, &t, &m);
    build(1, 1, n);
    while(m--) {
        char op;
        int l, r;
        scanf("\n%c%d%d", &op, &l, &r);
        // 这有一个坑,你懂的
        if(l > r){
            swap(l,r);
        }
        if(op == 'C') {
            int cor;
            scanf("%d", &cor);
            update(1, l, r, 1, n, cor);
        } else {
            int ans = query(1, l, r, 1, n);
            int cnt = 0;
            for(int i = 0; i < t ; i++) {
                int get = ans & (1 << i);
                if(get) {
                    cnt++;
                }
            }
            printf("%d\n", cnt);
            // printf("%d\n",ans);
            // print_bin(ans);
            // printf("\n");
        }
    }
    return 0;
}

第一次见到这题的时候直接懵逼,第二次见的时候直接秒了….嗯,真舒服,果然有些题做不出是因为火候不够,没必要硬上,等水平到了,回头一看简单得飞起。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值