线段树-POJ2777

这是一个统计问题,大意是给定两种操作,C A B color:将区间[A,B]涂上颜色color,P A B统计并打印区间[A,B]上颜色的数目,可图颜色的种类不超过30种,区间的长度最大可达100000,给出的操作次数最多可达100000.

这是一个经典的线段树题目,由于颜色的数目最多只有30种,所以用32位整形的每个位表示一种对应的颜色是可行的,这样便可以很方便做集合并的操作。

#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100100

using namespace std;

int l,t,o;
long long int color[N<<2];

int count(int k)//计算一个整数有多少个位为1
{
    int ans=0;
    while(k>0)
    {
          ans++;
          k-=(k&(-k));//右边的表达式的作用是将K的最后一个1独立出来
//这借用了树状数组里使用的位运算方法
    }
    return ans;
}

void update(int rt,int ld,int rd,int s,int e,long long int c)
{
     if(s<=ld && rd<=e) {
         color[rt]=c;
         return ;
     }
     int mid=(ld+rd)>>1;
     int cnt = count(color[rt]);
     if(cnt==1) {color[rt<<1]=color[rt];color[rt<<1|1]=color[rt];}
     if(e<=mid) update(rt<<1,ld,mid,s,e,c);
     else if(s>mid) update(rt<<1|1,mid+1,rd,s,e,c);
     else {
          update(rt<<1,ld,mid,s,mid,c);
          update(rt<<1|1,mid+1,rd,mid+1,e,c);
     }
     color[rt]=color[rt<<1]|color[rt<<1|1];
}

int query(int rt,int ld,int rd,int s,int e)
{
    if(s<=ld && rd<=e) return color[rt];
    int mid=(ld+rd)>>1;
    int cnt=count(color[rt]);
    if(cnt==1) return color[rt];
    if(e<=mid) return query(rt<<1,ld,mid,s,e);
    else if(s>mid) return query(rt<<1|1,mid+1,rd,s,e);
    else {
         int c1=query(rt<<1,ld,mid,s,mid);
         int c2=query(rt<<1|1,mid+1,rd,mid+1,e);
         return c1|c2;
    }
}

void getline(char *s)
{
     char ch;
     int i=0;
     while(true) {
         scanf("%c",&ch);
         s[i++]=ch;
         if(ch=='\n') break;
     }
     s[i]='\0';
}

int main()
{    
    while(scanf("%d%d%d",&l,&t,&o)!=EOF){
         char str[20],ch;
         for(int i=0;i<4*N;i++) color[i]=1;
         scanf("%c",&ch);
         for(int i=0; i<o; i++) {
              getline(str);
              int a,b,c;
              sscanf(str,"%c%d%d",&ch,&a,&b);
              int temp = a;
              if(a>b) {
                   a=b;
                   b=temp;
              }
              
              if (ch=='C') {
                   sscanf(str,"%c%d%d%d",&ch,&a,&b,&c);
                   update(1,1,l,a,b,1<<(c-1));
              }
              if(ch=='P') printf("%d\n", count(query(1,1,l,a,b)));
         }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值