pku 2777(经典线段树染色问题)

Count Color

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 41202   Accepted: 12458

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
题目意思:给三个数n,t,m n代表区间大小,t代表颜色的种类(没什么用) m代表询问次数 操作为‘C‘时代表更新区间值,操作为‘Q‘时代表询问区间内的颜色数目并输出。

运用了lazy思想和位运算 。。看了别人的思想打出来的代码。。对理解线段树真的十分有用lazy:只要插入的区间完全覆盖了当前结点所管理的区间就不再往下做了,在当前结点上打上一个lazy标记,然后直接返回。下次如果遇到当前结点有lazy标记的话,直接传递给两个儿子,自己的标记清空。好处就是满足条件时就不用更新到子节点,节约时间。位运算:用二进制来表示每一位的颜色(能够想出来的人真的要对算法非常了解,,,渣渣膜拜)比如说左边叶子节点被染成了3,右边的被染成了4则 父亲节点就被两种颜色染过了 00...0100 | 00...1000 = 00...1100 代表此区间被 3和4所染过了 ,这题颜色最多30种,所以能够用 int表示更多解释下面见代码:
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#define N 1100005
using namespace std;

const int MAXSIZE = 100005;
int sum;
struct Tree{
    int color; ///用二进制表示30种颜色,所以求每两个子区间的位或的结果就是父亲结点
    int cover ;///表示某区间的颜色是否相同
    int l,r;
}tree[MAXSIZE<<2];
void pushUp(int i){
    tree[i].color = tree[i<<1].color|tree[i<<1|1].color;
}
void pushDown(int i){
    if(tree[i].cover==1){
        tree[i<<1].color = tree[i].color;
        tree[i<<1|1].color = tree[i].color;
        tree[i<<1].cover = tree[i<<1|1].cover = 1;
        tree[i].cover = 0;
    }
}
void build(int l,int r,int idx){
    tree[idx].l = l;
    tree[idx].r = r;
    tree[idx].color = 1; ///刚开始颜色都为1
    tree[idx].cover = 1;  ///刚开始区间颜色都是相同的
    if(l==r) return ;
    int mid = (l+r)>>1;
    build(l,mid,idx<<1);
    build(mid+1,r,idx<<1|1);
    pushUp(idx);
}

void update(int l,int r,int idx,int val){
    if(tree[idx].l>=l&&r>=tree[idx].r){
        tree[idx].cover = 1;
        tree[idx].color=val;
        return;
    }
    pushDown(idx);
    int mid = (tree[idx].l+tree[idx].r)>>1;
    if(r<=mid) update(l,r,idx<<1,val);
    else if(l>mid) update(l,r,idx<<1|1,val);
    else {
        update(l,mid,idx<<1,val);
        update(mid+1,r,idx<<1|1,val);
    }
    pushUp(idx);
}
void query(int l,int r,int idx){
    if(tree[idx].l>=l&&tree[idx].r<=r){
        //printf("tree[%d].color = %d\n",idx,tree[idx].color);
        sum|=tree[idx].color;
        return;
    }
    if(tree[idx].cover==1){ ///该区间的颜色相同不需要往下分了
        sum|=tree[idx].color;
        return;
    }
    int mid = (tree[idx].l+tree[idx].r)>>1;
    if(r<=mid) query(l,r,idx<<1);
    else if(l>mid) query(l,r,idx<<1|1);
    else {
        query(l,mid,idx<<1);
        query(mid+1,r,idx<<1|1);
    }
}
int solve(){
    int ans = 0;
    //printf("sum = %d\n",sum);
    while(sum){
        if(sum&1)  ///如果sum的最低位是1则证明已经被染色
            ans++;
        sum = sum>>1;
    }
    return ans;
}

int main()
{
    int n,t,m;
    while(scanf("%d%d%d",&n,&t,&m)!=EOF){
        build(1,n,1);
        while(m--){
            char s[5];
            scanf("%s",s);
            if(s[0]==‘C‘){
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                update(a,b,1,1<<(c-1));  ///以二进制来表示颜色,比如染色成2 则二进制为00..010 即 2^1(^代表次方)
            }else{
                int a,b;
                sum = 0;
                scanf("%d%d",&a,&b);
                query(a,b,1);
                printf("%d\n",solve());
            }
        }
    }
    return 0;
}
				
时间: 2024-10-24 16:04:32

pku 2777(经典线段树染色问题)的相关文章

ZOJ1610 Count the Colors 经典线段树染色问题

题意,给你n个  x,y,c,意思就是区间[x,y]被染成C色,但是颜色会被覆盖的,染色操作完成以后 问你每种颜色有多少段 并输出颜色编号id跟段数cnt 经典问题,不过写的有点撮吧,没去看别人的,这个方法应该是最传统的最普通的,常规的开数组记录,也许大神们有更高端的方法 #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring>

POJ2528 Mayor&#39;s posters(线段树染色问题+离散化)

题目大意:有t组数据,每组数据给你n张海报(1<=n<=10000),下面n组数据分别给出每张海报的左右范围(1 <= l <= r <= 10000000),下一张海报会覆盖前一张海报,求最后可见(包括完全和不完全可见)的海报有几张. 例如: 1 5 1 4 2 6 8 10 3 4 7 10 如上图所示,答案为4. 解题思路:其实这是一道区间染色问题,但是由于查找区间太大,显然直接建树会导致MLE,所以这里通过使用对区间的离散化来缩小查找范围.参考了一些大牛博客,简单说一

poj 2777(线段树的节点更新策略)

1 /* 2 之前的思想是用回溯的方式进行颜色的更新的!如果用回溯的方法的话,就是将每一个节点的颜色都要更新 3 通过子节点的颜色情况来判断父节点的颜色情况 !这就是TLE的原因! 4 5 后来想一想没有必要 !加入[a, b] 区间有p管辖,那么tree[p]的颜色值就是[a, b]所有点的颜色值! 6 如果[a,b]的子区间[c,d]没被跟新,那么tree[p]也是[c,d]的值! 7 否则,在更新[c,d]区间的时候,一定会经过 p 点!然后由上到下更新p<<1 和 p<<1

NC15667 统计颜色(线段树染色)

一道线段树染色,但是这里是桶,我犯了经验主义以为是覆盖问题 数据不大,用二进制表示即可 #include<iostream> #include<algorithm> #include<stack> #include<vector> #include<cstring> using namespace std; typedef long long ll; const int N=1e5+10; const int mod=1e9+7; int n,m

poj 3667Hotel(经典线段树)

传送门:点击打开链接 题目大意: 有N个房间排在一列,有两种操作. 1:查询最靠左的长度为len的空房间,并且入住这些空房间. 2:以l开头,长度为r的房间退房.(如果本来就是空的 还是要退房). 解题思路: 一类经典的线段树题目.区间合并类. 容易想到在查询上做做手脚,这题就差不多了.问题在于维护哪些东西.由于两个子区间要求合并,那么可以记录一个前最长可用连续,后最长可用连续,和连续可用的最大值. 那么pushup函数和pushdown函数就很简单了. 下面就是查询:如果左区间的连续值是大于等

P2161 [SHOI2009]会场预约 - 线段树染色

是真的染色,把不同预约看做不同颜色,现在问题就是一个区间内不同颜色的数量,这个分块线段树都能做吧(不考虑复杂度用莫队也行) 注意,线段树的最大边界必须是定值,不能随输入改变(一开始懒得离线动态更新右端点然后节点的编号就串了) 注意数组大小,因为same和tag数组都是针对线段树节点设置的,所以其数组大小也要开4倍 #include <algorithm> #include <iostream> #include <cstring> #include <cstdio

贴海报 (线段树染色-离散化

n(n<=10000) 个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000) .求出最后还能看见多少张海报. 虽然之前学过离散化,但用的时候就想不起来 emm: 10000个海报 最多有10000个区间 20000个坐标值,远少于10000000,因此采用离散化 将离散化后的坐标对应数组下标储存到线段树中 : 染色区间是整段的,本身就可以看做 lazy标记 ,需要下推函数: 下推 : void push_down(int pos){ if( c

FZU2105 Digits Count(经典 线段树)

Digits Count Time Limit:10000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Description Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations: Operation 1: AND opn L R Here opn, L and R are integers. Fo

codeforces - 444c DZY Loves Colors(线段树+染色)

C. DZY Loves Colors time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output DZY loves colors, and he enjoys painting. On a colorful day, DZY gets a colorful ribbon, which consists of n units (they