You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##POJ 2777 Count Color

一、题目大意

题意:有L块连续的板子,每块板子最多染一种颜色,有T种(<=30)颜色,刚开始将所有板子染成颜色1O次操作(包括将[a,b]染成颜色k,和询问[a,b]的不同颜色数),输出每次询问的值。

二、题目分析

经典的区间染色问题。

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

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

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

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

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

三、实现代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;
const int N = 100010;

#define ls u << 1
#define rs u << 1 | 1

struct Node {
    int l, r;
    int add, sum;
} tr[N << 2];

void pushup(int u) {
    tr[u].sum = tr[ls].sum | tr[rs].sum; //因为是模拟二进制的操作,所以采用 或 运算即可完成拼接操作
}

void pushdown(int u) {
    if (tr[u].add) {
        tr[ls].add = tr[rs].add = tr[u].add;
        tr[ls].sum = tr[rs].sum = tr[u].add;
        tr[u].add = 0;
    }
}

void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) {
        tr[u].sum = 1; // sum记录的其实是一个30位的二进制压缩模拟数字此时置为1表示最后一位即0位为1是默认的颜色
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void modify(int u, int l, int r, int c) {
    if (l <= tr[u].l && tr[u].r <= r) {
        tr[u].add = 1 << (c - 1);
        tr[u].sum = 1 << (c - 1);
        return;
    }

    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify(u << 1, l, r, c);
    if (r > mid) modify(u << 1 | 1, l, r, c);
    pushup(u);
}

int query(int u, int l, int r) {
    if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;

    int ans = 0;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) ans = query(u << 1, l, r);
    if (r > mid) ans = ans | query(u << 1 | 1, l, r);
    return ans;
}

//手工版本数字1个数
int count(int x) {
    int cnt = 0;
    while (x) {
        cnt++;
        x -= (x & -x);
    }
    return cnt;
}

int main() {
    //加快读入
    ios::sync_with_stdio(false), cin.tie(0);
    int n, t, m; //区间[1,n],t种颜色,m个操作
    cin >> n >> t >> m;

    //构建
    build(1, 1, n);

    while (m--) {
        char op;
        int l, r;
        cin >> op >> l >> r;
        if (l > r) swap(l, r); //坑点题目没说哪个大哪个小,需要特判
        if (op == 'C') {
            int c;
            cin >> c;
            modify(1, l, r, c);
        } else {
            int ans = query(1, l, r);
            int cnt = count(ans); //获取某个数二进制位1的个数
            printf("%d\n", cnt);
        }
    }
    return 0;
}