ZOJ 1610一道疯狂踩坑的线段区间染色问题

题目

Painting some colored segments on a line, some previously painted segments may be covered by some the subsequent ones.
Your task is counting the segments of different colors you can see at last.

输入

The first line of each data set contains exactly one integer n, 1 <= n <= 8000, equal to the number of colored segments.
Each of the following n lines consists of exactly 3 nonnegative integers separated by single spaces:
x1 x2 c
x1 and x2 indicate the left endpoint and right endpoint of the segment, c indicates the color of the segment.
All the numbers are in the range [0, 8000], and they are all integers.
Input may contain several data set, process to the end of file.

输出

Each line of the output should contain a color index that can be seen from the top, following the count of the segments of this color, they should be printed according to the color index.
If some color can’t be seen, you shouldn’t print it.
Print a blank line after every dataset.

样例输入

5
0 4 4
0 3 1
3 4 2
0 2 2
0 2 3
4
0 1 1
3 4 1
1 3 2
1 3 1
6
0 1 0
1 2 1
2 3 1
1 2 0
2 3 0
1 2 1

样例输出

1 1
2 1
3 1
.
1 1
.
0 2
1 1
(’.'表示一个换行符)

思路

刚开始的思路是直接套用之前的染色模板,在查询的时候直接统计各种颜色的数量。但是这样是不对的,对于一个刚好卡在中间的区间,如果直接统计次数的话,那么一段颜色会统计两次,所以要深入到底层去统计每个结点的颜色,再将其分段取出。根据调用函数的顺序可以控制每个结点在统计数组中的位置是依次进行的。
在这里使用-1表示没有颜色或者多种颜色,这样虽然会浪费一定的时间到没有颜色的区间进行查询,但是可以节省一个lazy空间。
说实话,这道题在题目描述上挖的坑太多了,第一个坑就是输入的n,n表示有多少条线段,而不是总的线段长度,然后对于给定的区间竟然是左开右闭?找了半天才找到这个bug…

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=8050;
int color[maxn<<2];
int num[maxn];
int A[maxn];
int n;
void pushdown(int id){
	if(color[id]!=-1){
		color[id<<1]=color[id<<1|1]=color[id];
		color[id]=-1;
	}
}
void update(int id,int l,int r,int a,int b,int c){
	if(a<=l&&r<=b){
		color[id]=c;
		return;
	}
	pushdown(id);//由于用-1表示多种颜色或0种颜色所以对于标记进行下方之后统一设置为-1
	int mid=(l+r)>>1;
	if(a<=mid)update(id<<1,l,mid,a,b,c);
	if(b>mid)update(id<<1|1,mid+1,r,a,b,c);
}
int t=0;
void query(int id,int l,int r){
	if(l==r){//对于最下面的区间进行统计。
		A[t++]=color[id];
		return;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	query(id<<1,l,mid);
	query(id<<1|1,mid+1,r);
}
int main(){
	while(~scanf("%d",&n)){
		memset(color,-1,sizeof(color));
		int x,y,z;
		while(n--){
			scanf("%d%d%d",&x,&y,&z);
			update(1,1,8000,x+1,y,z);
		}
		memset(A,-1,sizeof(A));//初始值设置为-1表示没有颜色
		memset(num,0,sizeof(num));
		t=0;
		query(1,1,8000);
		for(int i=0;i<t;){//对于连续的颜色片段进行统计
			if(A[i]==-1){
				i++;
				continue;
			}
			num[A[i]]++;
			int j;
			for(j=i+1;j<t;j++){
				if(A[j]!=A[i])break;
			}
			i=j;
		}
		for(int i=0;i<=8000;i++){
			if(num[i])printf("%d %d\n",i,num[i]);
		}
		printf("\n");
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值