线段树 基础练习例子(二)

本文介绍了一种使用线段树解决颜色覆盖问题的方法,通过修改线段树的cover域定义,实现对不同颜色线段的统计。算法包括线段树的初始化、插入操作和计数过程,适用于解决桌子上看到的盒子数量问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:桌子上零散地放着若干个盒子,桌子的后方是一堵墙。如右图所示。问从桌子前方可以看到多少个盒子?假设人站得足够远。

分析:可以这样来看这道题:x轴上有若干条不同线段,将它们依次染上不同的颜色,问最后能看到多少种不同的颜色?(后染的颜色会覆盖原先的颜色)
我们可以这样规定:x轴初始是颜色0,第一条线段染颜色1,第二条线段染颜色2,以此类推。

原先构造线段树的方法不再适用,但是我们可以通过修改线段树的cover域的定义,使得这道题也能用线段树来解。
定义cover如下:cover=-1表示该区间由多种颜色组成。cover>=0表示该区间只有一种单一的颜色cover。

统计算法:使用一个数组Flag,初始化为0。遍历线段树,对于每种颜色c对Flag[c]赋值1。最后统计Flag中1的个数即可。(注意颜色0应该排除在外,可以在最后减1)

program:

#include<iostream>
using namespace std;
#define maxn 10000
int num[maxn*2-1];
struct linetree
{
  int a,b;
  int cover;   
}d[maxn*2-1];
void make_tree(int p,int a,int b)
{//初始化二叉树是一样道理的
   int mid=(a+b)/2;
   d[p].cover=0;
   d[p].a=a;
   d[p].b=b;
   //cout<<"p a b "<<a<<' '<<b<<endl;
   if(a+1==b)
     return;
   
   make_tree(p<<1,a,mid);
   make_tree(p<<1|1,mid,b);
       
}
void line_insert(int p,int a,int b,int c)
{
  int mid=(d[p].a+d[p].b)/2;
  if(d[p].a==a&&d[p].b==b)
     {//如果是完全覆盖的话当然就不必要继续往下面探究了啦。
       d[p].cover=c;
       //cout<<"p a b "<<a<<' '<<b<<endl;
       return;                    
     }
  if(d[p].cover>=0)
     {

//如果不是完全覆盖的话,而如果进入的区域是单色或者无色。//那么就要儿子继承父亲的颜色,是的等会下来的颜色该覆盖到哪就哪里
       d[p<<1].cover=d[p].cover;
       d[p<<1|1].cover=d[p].cover;
       d[p].cover=-1; 

  //并且把该区域设置为多色,这样的原因是让人知道该区域是多色,而且原来已经发生过继承的事情了          
     }


//对一个不是全部覆盖该区间的颜色而言,它要做的就是去打算涂色该区间
//子区间,如果该区间是单色或者无色,子区间肯定要继承父亲的颜色
//以此类推,直到完全覆盖

 

 

//以1~4,2~5,3~4作为例子
//如果已经继承,相当于在1~5区间是多颜色-1,除非下一对数据进来是
//完全覆盖1~5区间的,否则,在多色的情况下,再增加一种颜色,
//还是多色,即对于-1颜色区间是不用考虑继承颜色的

 


  if(b<=mid)
     line_insert(p<<1,a,b,c);
  else if(a>=mid)
     line_insert(p<<1|1,a,b,c);
  else {
         line_insert(p<<1,a,mid,c);
         line_insert(p<<1|1,mid,b,c);
        
       }
 
      
}
void cnt(int p)
{
   cout<<"=========                                 p d[p].a d[p].b "<<d[p].a<<' '<<d[p].b<<endl;
   if(d[p].cover>0)
    {
          num[d[p].cover]=1;
       cout<<"p d[p].a d[p].b "<<d[p].a<<' '<<d[p].b<<endl;
       return;//统计的时候,完全覆盖也会回头,不一定是都在叶子节点才结束  
    }
   if(d[p].a+1==d[p].b)
       return;//当然如果不是因为完全覆盖回头,那么就是因为叶子节点回头
   
   cnt(p<<1);
   cnt(p<<1|1);
       
     
}
int main()
{
int test,n,m;
cin>>test;
while(test--)
{
  cin>>n>>m;  //n对数据,1~m是桌子单位长度
  make_tree(1,1,m);
  memset(num,0,sizeof(num));
  int k=1;
  for(int i=0;i<n;i++)
  {
    int a,b;
    cin>>a>>b;    
    line_insert(1,a,b,k++);  
  }
  int sum=0;
  cnt(1);
  for(int i=0;i<(m<<1);i++)
    if(num[i]==1)
       sum++;
  cout<<sum<<endl;
  
            

system("pause");
return 0;}

 



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值