题目大意:桌子上零散地放着若干个盒子,桌子的后方是一堵墙。如右图所示。现在从桌子的前方射来一束平行光, 把盒子的影子投射到了墙上。问影子的总宽度是多少?
这道题目是一个经典的模型。在这里,我们略去某些处理的步骤,直接分析重点问题,可以把题目抽象地描述如下:x轴上有若干条线段,求线段覆盖的总长度。
给线段树每个节点增加一个域cover。cover=1表示该结点所对应的区间被完全覆盖,cover=0表示该结点所对应的区间未被完全覆盖。
program:
#include<iostream>
using namespace std;
int sum;
int n,m;
struct linetree
{
int a,b;
int cover;
}d[2*(100-1)-1];
void make_tree(int p,int a,int b)
{
//if(a==b)return ;
d[p].a=a;//因为判断返回的一层提早了一层,所以判断要在记录之后。
d[p].b=b;
d[p].cover=0;
cout<<"ab "<<a<<' '<<b<<endl;
cout<<"d[p].a d[p].b "<<d[p].a<<' '<<d[p].b<<endl;
if(a+1==b)return ;//这个是线段树的特征,因为右孩子没有像mid+1这样操作,所以最终在叶子节点的右孩子是不会出现mid=b的。从而出现死循环
int mid= (a+b)/2;
cout<<"mid "<<mid<<endl;
make_tree(p*2,a,mid);
make_tree(p*2+1,mid,b);
}
void line_insert(int p,int a,int b)
{
int mid=(d[p].a+d[p].b)/2;
if(d[p].cover==1)return;//如果已经完全覆盖就可以不用再管,因为又不是涂颜色,只是计算长度而已
if(a==d[p].a&&b==d[p].b)
{
d[p].cover=1;//这个操作可以细化到每一个单位长度,所以也是最底层的操作
return;
}
else if(b<=mid)//如果插入的数据全部在左孩子,就直接在左孩子中找
line_insert(p<<1,a,b);
else if(a>=mid)
line_insert(p<<1|1,a,b);//同上换右孩子
else //否则用mid砍断分开左孩子右孩子来找
{
line_insert(p<<1,a,mid);
line_insert(p<<1|1,mid,b);
}
}
void cnt(int p)
{
cout<<"========= p d[p].a d[p].b "<<d[p].a<<' '<<d[p].b<<endl;
if (d[p].cover==1)
{
cout<<"p d[p].a d[p].b "<<d[p].a<<' '<<d[p].b<<endl;
sum+=(d[p].b-d[p].a);//一开始只是sum++而已,晕
return;
}
if(d[p].a+1==d[p].b)//用这个控制到叶子节点已经足够
return;
//else{
//if((p<<1)<=((m-1)<<1-1) )
cnt(p<<1);
//if((p<<1|1)<=((m-1)<<1-1))
cnt(p<<1|1);
// }
}
int main()
{
int test;
cin>>test;
while(test--)
{
cin>>n>>m;
make_tree(1,1,m);//1,是数组的第一个数。1是数轴的开端,m末端
int aa,bb;
for(int i=0;i<n;i++)
{
cin>>aa>>bb;
line_insert(1,aa,bb);//每输入一对数据,就要更新二叉树
/* sum=0;
cnt(1);
cout<<sum<<endl; */
}
sum=0;
cnt(1);
cout<<sum<<endl;
}
system("pause");
return 0; }
总结:线段树就是初始化一棵二叉树,然后通过插入进行更新二叉树的信息,最后再进行统计