POJ 2777(线段树 位运算)

POJ 2777

题目大意

有一个长位L的区间,有T种颜色,进行O次操作,每次操作 (1n100000,1T30,1O100000)

每次操作有两种形式:

P A B C:将区间(A,B)染成颜色C

Q A B:询问区间(A,B)有多少种不同的颜色

每个点的初始颜色都是1

注意:A可能大于B

分析

线段树的区间更新

有两种信息,一个是区间维护的信息color表示某个区间有哪些颜色,还有一个信息是延迟标记信息lazy数组。

技巧

这道题可以用位运算来进行线段间的合并操作,就是用用一个32位int类型的col变量表示某个区间有哪些颜色

知道两个子节点有哪些颜色要求父亲节点有哪些颜色的时候通过按位或(|)进行合并.

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
const int MAXN=100005;
int n,s,m;//线段长度为n,一共有s种颜色,进行m种操作
int lazy[MAXN*4];//延迟标记
int col[MAXN*4];
void Init()
{
      memset(lazy,0,sizeof(lazy));
}
void Pushup(int rt)//rt的子节点已经更新好了,现在更新rt节点
{
      col[rt]=col[rt*2] | col[rt*2+1];
}
void Build(int l,int r,int rt)
{
    if(l==r)
    {
          col[rt]=2;//将颜色1也就是二进制1位染色
          return ;
    }
    int m=(l+r)/2;
    Build(l,m,rt*2);
    Build(m+1,r,rt*2+1);
    Pushup(rt);
}
void Pushdown(int rt)//节点rt已经更新好了,将标记下推
{
    if(lazy[rt]!=0)
    {
          col[rt*2]=(1<<lazy[rt]);
          col[rt*2+1]=(1<<lazy[rt]);

          lazy[rt*2]=lazy[rt];
          lazy[rt*2+1]=lazy[rt];
          lazy[rt]=0;
    }
}
void Update(int L,int R,int x,int l,int r,int rt)
{
    if(L<=l && r<=R)
    {
         col[rt]=(1<<x);
         lazy[rt]=x;
         return;
    }
    int m=(l+r)/2;
    Pushdown(rt);
    if(L<=m)Update(L,R,x,l,m,rt*2);
    if(R>m)Update(L,R,x,m+1,r,rt*2+1);
    Pushup(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(L<=l && r<=R)
    {
          return col[rt];
    }
    int color1,color2;
    int  color_ans;
    color1=0;
    color2=0;
    int m=(l+r)/2;
    Pushdown(rt);
    if(L<=m)color1=Query(L,R,l,m,rt*2);
    if(R>m)color2=Query(L,R,m+1,r,rt*2+1);
    color_ans=color1 | color2;
    return color_ans;
}
void find_ans(int t)
{
     int ans=0;
     while(t>0)
     {
           if(t%2==1)ans++;
           t=t/2;
     }
     cout<<ans<<endl;
}
int main()
{
    char c;
    int L,R,x;
    int ans;
    int t;
    while(scanf("%d%d%d",&n,&s,&m)!=EOF)
    {
          Init();
          Build(1,n,1);
          for(int i=1;i<=m;i++)
          {
                scanf("%c",&c);
                while(c==' '|| c=='\n')scanf("%c",&c);
                if(c=='C')
                {
                    scanf("%d%d%d",&L,&R,&x);
                    if(L>R){t=L;L=R;R=t;}//题目中可能有L>R的情况
                    Update(L,R,x,1,n,1);
                }
                else if(c=='P')
                {
                    scanf("%d%d",&L,&R);
                    if(L>R){t=L;L=R;R=t;}//题目中可能有L>R的情况
                    ans=Query(L,R,1,n,1);
                    find_ans(ans);
                }
          }
    }
    return 0;
}
/*
6 7 4
C 1 6 2
P 1 5
C 4 2 7
P 6 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值