【学校OJ】 线段树 影子的宽度&盒子的个数

【影子的宽度】

题目描述

桌子上零散地放着若干个盒子,盒子都平行于墙。桌子的后方是一堵墙。如图所示。现在从桌子的前方射来一束平行光, 把盒子的影子投射到了墙上。问影子的总宽度是多少?

输入

第1行:3个整数L,R,N。-100000 <=L<=R<= 100000,表示墙所在的区间;1<=N<=100000,表示盒子的个数
接下来N行,每行2个整数BL, BR,-100000 <=BL<=BR<= 100000,表示一个盒子的左、右端点(左闭右开)

输出

第1行:1个整数W,表示影子的总宽度。

样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

Sample Input 1
0 7 2
1 2
4 5

Sample Input 2
-10 10 2
-5 2
-2 2


Sample Input 3
-10 10 3
-7 0
-4 9
-4 2


Sample Input 4
-100 100 3
-7 2
5 9
2 5

Sample Input 5
-50 50 4
-2 4
0 6
9 10
-5 30

样例输出

Sample Output 1
2

Sample Output 2
7

Sample Output 3
16

Sample Output 4
16

Sample Output 5

35

【盒子的个数】

题目描述

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

输入

第1行:3个整数L,R,N。-100000 <=L<=R<= 100000,表示墙所在的区间;1<=N<=100000,表示盒子的个数
接下来N行,每行2个整数BL, BR,-100000 <=BL<=BR<= 100000,表示一个盒子的左、右端点(左闭右开)。越在前面输入的盒子越排在离墙近的位置,后输入的盒子排在离墙远的位置。

输出

第1行:1个整数M,表示可看到的盒子个数。

样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

1 10 5
2 6
3 6
4 6
1 2
3 6

样例输出

3


    这两道题很久以前就AC了,现在来当作模板题总结一下线段树的写法。

    对于这两道孪生题目,也是不能说什么了……连图都节约资源用同一张……那么就总结一下吧!

    对于第一题,相较于第二题还是很简单的,先建造出[L,R]的线段树(建树就不慢慢说了),然后用一个成员p记录区间状态:1=>全盖、0=>全裸、-1=>半盖半裸(有点像四象树)。

    每次如果完全覆盖一个区间,不管怎样都将这个区间标记为全盖,然后返回。而如果无法完全覆盖,就分成左子区间和右子区间,递归操作。

    但请注意,因为每次覆盖时都使用了‘懒操作’(lazy),其实我们只改变了最大一个区间的状态值,每次递归时需要将新的状态传递给儿子,并改变儿子,才能避免出错。同时,进行最后的查询用递归固然方便,但每次都有可能花费O(n)的时间,对于多次操作,我们可以新增一个成员sum,记录区间内的影子宽度,方便查询,不过要记得和p一起维护哦。

#include
#include
using namespace std;
const int M=100000;
struct node
{
	int p,l,r;
}A[8*M+5];
int K[M+5][2];
int ml,mr,n;
void make(int i,int l,int r)
{
	A[i].l=l;
	A[i].r=r;
	if(l==r)return;
	make(i*2,l,((l+r)>>1));
	make(i*2+1,((l+r)>>1)+1,r);
}
void insert(int q,int l,int r)
{
	if(l<=A[q].l&&r>=A[q].r)
	{
		A[q].p=A[q].r-A[q].l+1;
		return;
	}
	if(A[q].l>r||A[q].r
    而对于第二题,我们必须换一种思路。我们这次的小p就不能只承担0、1、-1的任务了,它现在有很多种状态:a(a>0)表示区间的盒子全为a这一个、0表示区间没有任何盒子、-1表示区间内盒子混杂,不止一个。然后每次用同样的方法去将一个区间进行覆盖(同样需要下传p)。然后最后的时候,实在没办法使用可爱的sum(因为盒子被遮挡,可能在区间内不连续,甚至被隔成几段),我们进行一次O(n)的递归,将它们标记进数组就可以用一次循环进行统计了。

#include
#include
using namespace std;
const int M=100000;
struct node
{
	int color,l,r;
}A[8*M+5];
int K[M+5][2];
int vis[M+5];
int ml,mr,n,ans;
void make(int i,int l,int r)
{
	A[i].l=l;
	A[i].r=r;
	if(l==r)return;
	make(i*2,l,((l+r)>>1));
	make(i*2+1,((l+r)>>1)+1,r);
}
void insert(int q,int l,int r,int c)
{
	if(l<=A[q].l&&r>=A[q].r)
	{
		A[q].color=c;
		return;
	}
	if(A[q].l>r||A[q].r=0)
	{
		A[2*q].color=A[2*q+1].color=A[q].color;
		A[q].color=-1;
	}
	if(A[q].l0)
	{
		if(!vis[A[q].color])
		{
			ans++;
			vis[A[q].color]=1;
		}
	}
	else
	{
		if(A[q].color==-1)
		{
			sum(2*q);
			sum(2*q+1);
		}
	}
}
int main()
{
	scanf("%d%d%d",&ml,&mr,&n);
	mr--;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&K[i][0],&K[i][1]);
		K[i][1]--;
	}
	make(1,ml,mr);
	for(int i=1;i<=n;i++)
		insert(1,K[i][0],K[i][1],i);
	sum(1);
	printf("%d",ans);
}


你可能感兴趣的:(-->数据结构干瞪眼<--,结构,--,线段树)