POJ2777(线段树)Count Color

来源:互联网 发布:淘宝店面装修尺寸 编辑:程序博客网 时间:2024/06/03 00:37


Count Color
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 43131 Accepted: 13068

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 4C 1 1 2P 1 2C 2 2 2P 1 2

Sample Output

21

题意就是给你L(板块长度),T(颜色种类),O(操作次数),操作分为C和P,C之后跟有3个数A、B、C把区间A-B涂上C颜色。然后P后面跟有A、B,问你区间A-B有多少种颜色。

线段树区间更新,我们可以看到颜色给了30种,很明显是要让我们用二进制表示颜色种类哒嘛,也就是每种颜色有(1)或没有(0),共有2^30 种可能。

这次依旧是先加标记变量,每次更新并不需要更新到叶子结点(表示还试过更新到叶子,表示直接re啊喂,就是要学习lazy!!),具体我会在代码中说,总结很多前辈的经验,然后优化了一些地方。最坑的是要注意看题啊,A还有可能比B大哒!!

#include <iostream>#include <cstdio>#include <cmath>#include <algorithm>#include <queue>#include <map>#include <string>#include <cstring>#include <vector>using namespace std;const int MAXN=100000+10;int n,m,t;struct node{    int l,r;    int flag;//记录区间是不是完全覆盖    int colour;//记录结点颜色的种类}tree[MAXN<<2];int f(int x)//返回二进制1的个数{    int sum=0;    while(x)    {        if(x&1)sum++;        x>>=1;    }    return sum;}void build(int i,int l,int r)//建立线段树{    tree[i].l=l;    tree[i].r=r;    if(l==r)return;    int mid=(l+r)>>1;    build(i<<1,l,mid);    build(i<<1|1,mid+1,r);}void add(int i,int l,int r,int c)//更新线段树{    if(tree[i].l==l&&tree[i].r==r)//找到区间后,标记更新,暂时不向下更新(我试过更新到底直接RE,估计是爆栈了)    {        tree[i].flag=1;        tree[i].colour=1<<(c-1);        return ;    }    if(tree[i].flag)            //更新结点的时候,如果这个结点上次应该更新却没有更新,那么需要先向下更新一层结点                                //(因为这次更新走到这说明是需要向下更新的,如果之前还不更新,那么父亲结点肯定是会更新错误的)    {        tree[i].flag=0;        tree[i<<1].flag=tree[i<<1|1].flag=1;        tree[i<<1].colour=tree[i<<1|1].colour=tree[i].colour;    }    //然后继续向下更新这次的更新    int mid=(tree[i].l+tree[i].r)>>1;    if(r<=mid)add(i<<1,l,r,c);    else if(l>mid)add(i<<1|1,l,r,c);    else    {        add(i<<1,l,mid,c);        add(i<<1|1,mid+1,r,c);    }    //向下更新孩子完毕后,注意要向上更新父亲结点!(不要光管下面忘了上面呐)    tree[i].colour=tree[i<<1].colour|tree[i<<1|1].colour;}int ask(int i,int l,int r)//查找函数,没有特别好说的{    if(tree[i].l==l&&tree[i].r==r)    {        return tree[i].colour;    }    int mid=(tree[i].l+tree[i].r)>>1;    if(tree[i].flag)return tree[i].colour;    //不仅仅是优化,它下面还有可能没更新呢    if(r<=mid)return ask(i<<1,l,r);    else if(l>mid)return ask(i<<1|1,l,r);    else return ask(i<<1,l,mid)|ask(i<<1|1,mid+1,r);}int main(){    scanf("%d%d%d",&n,&t,&m);    char s[2];    int l,r,c;    build(1,1,n);    tree[1].flag=1;       //开始整个区间就是覆盖颜色1的    tree[1].colour=1;    while(m--)    {        scanf("%s",s);        if(s[0]=='C')        {            scanf("%d%d%d",&l,&r,&c);            if(l>r)swap(l,r);         //注意!看题目,l可能是大于r哒            add(1,l,r,c);        }        else        {            scanf("%d%d",&l,&r);            if(l>r)swap(l,r);            int ans=f(ask(1,l,r));            printf("%d\n",ans);        }    }    return 0;}

关于颜色更新覆盖问题,肯定是后来者居上,线段树就是大区间在上,如果走不到下面小区间,那么再次更新的时候,大区间就会更新覆盖小区间,然后再去重新更新小区间,最后返回更新大区间。也就是说开始第一次并不向下面的小区间更新,只是记录,下次走到这里的时候,需要用到它的小区间了,那么小区间才会被更新。如果走不到小区间,那么小区间根本不需要更新,大区间标记全覆盖就可以了,下次用到的时候自然会先更新。(这里还是需要大家自己画图多想一想,搞明白,只有深刻理解,才能把它转化为自己的资源再利用~)


1 0