ZOJ 1160

线段树习题,利用了延迟标记,与普通线段树频发查询不同,这里只进行了一次查询,所以事实上只是利用了线段树的结构和延迟标记的思想

几个注意点:

  • 要注意给出的n是线段涂色的次数,而非线段总长度,线段树的区间一直是[0, 8000]
  • 因为我是利用了map方便最终的计数,需要引入全局变量fom来记录上一个节点线段的颜色,同时还要注意访问到长度为一的线段时,要注意即使此时颜色为none,这是后也要修改全局变量,因为中间断开了的同颜色线段是算做两条线段,WA了好久的教训
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;

const int maxn = 8e3+5;
const int maxl = maxn<<2;
const int none= -1;
const int L= 0;
const int R= 8000;

typedef map<int, int> cRec;
map<int, int> segCnt;
int tree[maxl];
int s, e, fom; // s, e represent colored segment

inline int LeftChild(int i)
{
	return i<<1;
}
inline int RightChild(int i)
{
	return i<<1|1;
}
void Init()
{
	memset(tree, none, sizeof(tree));
	segCnt.clear();
	fom= none;
}
void PushDown(int rt, int l, int r)
{
	if (none== tree[rt] || l+1>= r){
		return;
	}
	int lc= LeftChild(rt), rc= RightChild(rt), c= tree[rt];
	tree[lc]= tree[rc]= c;
	tree[rt]= none;
}
void Update(int rt, int l, int r, int c)
{
	if (l>= r){
		return;
	}
	if (l>= s && r<= e){
		tree[rt]= c;
		return;
	}

	PushDown(rt, l, r);
	int mid= (l+r)>>1;
	if (s< mid){
		Update(LeftChild(rt), l, mid, c);
	}
	if (mid< e){
		Update(RightChild(rt), mid, r, c);
	}
}
void Check(int rt, int l, int r)
{
	if (none!= tree[rt]){
		if (fom!= tree[rt]){
			fom= tree[rt];
			++segCnt[fom];
		}
		return;
	}
	if (r== l+1){
		fom= none;
		return;
	}
	int mid= (l+r)>>1;
	Check(LeftChild(rt), l, mid);
	Check(RightChild(rt), mid, r);

}
inline void Ans()
{
	for (cRec::iterator iter= segCnt.begin(); segCnt.end()!= iter; ++iter){
		printf("%d %d\n", iter->first, iter->second);
	}
	putchar('\n');
}

int main(int argc, char const *argv[])
{
	int n, c;
	while (EOF!= scanf("%d", &n)){
		Init();
		while (n--){
			scanf("%d %d %d", &s, &e, &c);
			Update(1, L, R, c);
		}
		Check(1, L, R);
		Ans();

	}
	
	return 0;
}
posted @ 2020-07-29 15:01  IdiotNe  阅读(115)  评论(0编辑  收藏  举报