Count Color(线段树)

本文介绍了一种使用线段树数据结构解决区间染色及查询不同颜色数量问题的方法。通过构建线段树并定义节点状态表示颜色的存在状态,实现区间染色更新和快速查询区间内不同颜色的数量。

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次操作,初始每个球颜色为1, C为修改a,b区间的颜色为c, P表示询问a,b区间的颜色种数。

题解:线段数,tree[node]表示颜色的状态,0表示不存在,1表示存在

//#include"bits/stdc++.h"
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a,b) memset(a,b,sizeof(a))
#define lson l, mid, node << 1
#define rson mid + 1, r, node << 1 | 1
const int INF  =  0x3f3f3f3f;
const int O    =  1e6;
const int mod  =  10007;
const int maxn =  1e5+5;
const double PI  =  acos(-1.0);
const double E   =  2.718281828459;

const int _ = 1;
const int __ = 2;
const int ___ = 3;

int tree[maxn<<2];
int lazy[maxn<<2];

int pushup(int sta1, int sta2) { return sta1 | sta2; }

void pushdown(int l ,int r, int node){
    if(lazy[node]){
        tree[node<<1] = tree[node<<1|1] = lazy[node];
        lazy[node<<1] = lazy[node<<1|1] = lazy[node];
        lazy[node] = 0;
    }
}

void build(int l, int r, int node){
    if(l == r) {
        tree[node] = 2;
        lazy[node] = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson); build(rson);
    tree[node] = pushup(tree[node<<1] , tree[node<<1|1]);
}

int ql, qr, v;

void update(int l, int r, int node){
    if(ql <=l && qr >= r) {
        tree[node] = 1 << v;
        lazy[node] = 1 << v;
        return ;
    }
    pushdown(l ,r, node);
    int mid = (l + r) >> 1;
    if(qr > mid) update(rson);
    if(ql <= mid) update(lson);
    tree[node] = pushup(tree[node<<1], tree[node<<1|1]);
}

int query(int l, int r, int node){
    if(ql <= l && qr >= r) return tree[node];
    pushdown(l, r, node);
    int ans = 0;
    int mid = (l + r) >> 1;
    if(qr > mid) ans |= query(rson);
    if(ql <= mid) ans |= query(lson);
    return ans;
}

int main(){
    int l, t, q;
    while(~scanf("%d%d%d", &l, &t, &q)){
        build(1, l, 1);
        while(q --) {
            char c; scanf(" %c", &c);
            if(c == 'C') {
                scanf("%d%d%d", &ql, &qr, &v);
                if(ql > qr) swap (ql, qr);
                update(1, l, 1);
            }
            else if(c == 'P'){
                scanf("%d%d", &ql ,&qr);
                if(ql > qr) swap (ql, qr);
                int sta = query(1, l, 1);
                int ans = 0;
                while(sta) {
                    if(sta & 1) ans ++;
                    sta >>= 1;
                }
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}

 

<think>好的,我现在需要帮助用户调试他们的C++代码,解决洛谷P1558色板游戏的问题。用户提到使用线段树和位运算,但代码可能存在错误。首先,我需要仔细理解问题要求和常见的实现方式。 根据引用[2],线段树的每个节点需要用color懒标记来记录是否需要覆盖颜色,以及sum来记录当前区间的颜色种类,用二进制位表示。比如,如果color是有效的(非0),说明该区间被统一涂成某种颜色,此时sum就是对应的位。合并子节点时,sum通过或运算得到颜色种类的总数。 接下来,我需要检查用户提供的代码是否符合这些要点。常见的错误可能包括: 1. **线段树结构设计**:是否正确使用color懒标记和sum?例如,pushdown时是否清除了子节点的懒标记,并正确传递颜色。 2. **位运算处理**:是否将颜色转换为正确的位表示?例如,颜色x应该对应1 << (x-1),因为颜色从1开始,而二进制位从0开始。 3. **区间更新与查询**:在更新和查询时,是否正确处理区间覆盖和合并操作?尤其是当遇到A > B的情况时,是否交换了A和B? 4. **输入处理**:是否处理了输入的C命令和P命令,尤其是读取字符时是否有换行符或空格的问题? 现在,假设用户提供的代码可能存在以下问题: - **颜色转换错误**:例如,颜色x对应的位应该是1左移(x-1),而不是x本身,因为颜色从1开始,而二进制位从0开始。比如颜色1对应第0位,即1<<0=1。 - **懒标记处理不当**:在pushdown时,是否将父节点的color传递给子节点,并更新子节点的sum?同时,父节点的color在传递后应该被清除,即置为0。 - **区间查询合并结果**:在查询时,合并左右子区间的结果是否使用按位或运算,而不是相加或其他方式。 - **输入格式处理**:题目中提到A可能大于B,需要交换,代码中是否处理了这种情况?另外,读取命令时,可能因为空格或换行符导致读取错误,比如使用cin或scanf时需要注意。 例如,用户代码中的update函数可能在设置颜色时没有正确转换位,比如直接使用x而不是1<<(x-1)。此外,在pushdown函数中,如果没有正确将父节点的color传递给子节点,并更新sum,可能导致颜色覆盖失败。另外,在build函数中,初始颜色应为1,对应的位是1<<0,即1,而不是直接设置为1的颜色值。 假设用户代码中的颜色处理部分有误,例如将颜色x存为1 << x,那么当x=1时,得到的是2,而正确应该是1。这会导致后续的颜色统计错误。 此外,在查询时如果没有处理A > B的情况,直接使用区间可能导致错误。例如,在读取命令后,应该比较A和B,如果A > B则交换它们。 另外,线段树的节点数需要足够大,通常是原数据长度的4倍,否则可能导致数组越界。 总结可能的问题点: 1. 颜色转换错误,未将颜色x转换为1 << (x-1)。 2. 懒标记处理不当,未在pushdown时正确更新子节点的color和sum。 3. 未处理A > B的情况,导致区间错误。 4. 输入处理时未正确读取命令,尤其是字符读取时可能吸收了换行符或其他空格。 接下来,我需要根据这些可能的错误点,指导用户检查他们的代码,并给出修改建议。例如,检查颜色转换部分,确保是1左移(x-1);检查pushdown函数是否正确处理了懒标记的传递;检查是否在命令处理时交换了A和B;以及确认线段树的大小是否足够。</think>根据引用[2][4]中色板游戏的实现要点,分析线段树+位运算的常见代码错误及调试方法: ### 一、核心错误点排查 1. **颜色编码错误** 正确实现:颜色x对应二进制位$1<<(x-1)$ 错误示例:`sum = 1<<x`(应为`x-1`) 若初始颜色为1,则叶节点初始值应为$1<<0=1$ 2. **懒标记处理(关键错误高发区)** ```cpp void pushdown(int node) { if(tree[node].color) { // 必须同时更新左右子节点的color和sum tree[LEFT].color = tree[node].color; tree[RIGHT].color = tree[node].color; tree[LEFT].sum = tree[node].color; // 不是直接赋值colortree[RIGHT].sum = tree[node].color; tree[node].color = 0; // 清除标记 } } ``` 3. **区间合并运算** 查询时合并结果应为`left_sum | right_sum`,例如: ```cpp int query(int node,int l,int r) { if(...) return tree[node].sum; pushdown(node); return query(left_node) | query(right_node); // 正确合并方式 } ``` ### 二、数据边界处理 1. **输入交换处理**(引用[4]明确要求) ```cpp if(a > b) swap(a,b); // 在每次C/P操作时处理 ``` 2. **线段树大小** 数组大小应设为`4*MAXN`,例如: ```cpp const int MAXN = 1e5+10; struct Node { int sum, color; } tree[MAXN<<2]; // 正确的大小计算 ``` ### 三、输入输出陷阱 1. **字符读取处理** 建议使用`scanf(" %c")`过滤空白符: ```cpp scanf(" %c %d %d",&op,&a,&b); // 注意%c前的空格 ``` 2. **颜色数量计算** 输出时计算二进制中1的个数: ```cpp bitset<32> res(ans); cout << res.count() << endl; ``` ### 四、调试建议 1. 测试用例1: ``` 输入: 1 3 3 C 1 1 2 P 1 1 输出应为:1(二进制10) ``` 2. 测试用例2: ``` 输入: 3 3 4 C 1 2 1 C 2 3 2 P 1 3 输出应为:2(二进制110) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值