看完线段树本来打算找这道基础题练手,但是悲惨的砸进去2个半小时。

本来打算写个线段树的总结,看来也没时间了。

改了七次才AC,ZOJ又狂抽风。泪奔。

最BT的是最后一次犯错的代码竟然是TLE掉,不禁感叹牛逼~(见注释⑦处)


另看了下别人的代码,都比较短,思路不太一样。什么情况,明天再看看~


代码烂注释,慎入

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
inline int Rint() {int x; scanf("%d", &x); return x;}

#define MAXN 8000
#define BLANK -1
#define MIX -2
struct node
{
	int l, r;
				//!⑥	0也可以是颜色,不能拿来当 无色(特殊值)
	int c;		//color     混合=-2 无色=-1 颜色=0/1/2/3....
}a[MAXN*3];		//开3倍
//a[MAXN*2+10];		//!①注意 这里开两倍是不够的,因为一般不会是满二叉,如[0,3]


void init(int l, int r, int i)	//CALL: init(0, 8000, 1);
{//i=index
	a[i].c = BLANK; a[i].l = l; a[i].r = r;
	if(r-l > 1)	//大于1 就还要分		//	if(l+1 != r) 由于是整数,这样也可以
	{
		int mid = (l+r)/2;
		init(l, mid, i*2);
		init(mid, r, i*2+1);
	}
}
void clear(int i)		//CALL: clear(1)
{
	a[i].c = BLANK;
	if(a[i].r-a[i].l >1)
	{
		clear(i*2);
		clear(i*2+1);
	}
}
void insert(int x1, int x2, int c, int i)	//CALL: insert(x1, x2, c, 1)
{
	if(x2 == x1) return;		//点

	int mid = (a[i].l+a[i].r)/2;				//!④mid不事先放在node里,造成很多麻烦

	if(a[i].c == c) return;
	else if(x1 == a[i].l && x2 == a[i].r)	//正好覆盖
	{
		a[i].c = c;

//★★★ ⑦ 以下这样是不行的,遍历太多,直接TLE,违背线段树初衷!
/*
		//!②递归更新
		if(a[i].r-a[i].l >1)		//!⑤不是叶子结点,才要更新
		{
			insert(a[i].l, mid, c, i*2);	
			insert(mid, a[i].r, c, i*2+1);
		}
*/
	}
	else
	{
		int oldColor = a[i].c;
		a[i].c = MIX;		//此区间不是单纯的色,即混合 c=-2

		if(mid <= x1)		//mid x1 x2
		{
//printf("insert(%d, %d, %d, 右子树[%d,%d]);\n",x1, x2, c, a[i*2+1].l, a[i*2+1].r);
			insert(x1, x2, c, i*2+1);

			if(oldColor != BLANK && oldColor != MIX)			//!⑦  不应在完全覆盖时递归更新,而应选择在这里维护少量数据!
			{
				insert(a[i].l, mid, oldColor,  i*2);
				insert(mid, x1, oldColor,  i*2+1);
				insert(x2, a[i].r, oldColor,  i*2+1);
			}
		}
		else if(x2 <= mid)	//x1 x2 mid
		{
//printf("insert(%d, %d, %d, 左子树[%d,%d]);\n",x1, x2, c, a[i*2].l, a[i*2].r);
			insert(x1, x2, c, i*2);

			if(oldColor != BLANK && oldColor != MIX)
			{
				insert(a[i].l, x1, oldColor, i*2);
				insert(x2, mid, oldColor,  i*2);
				insert(mid, a[i].r, oldColor,  i*2+1);
			}
		}
		else		//x1 mid x2
		{
//printf("insert(%d, %d, %d, 左:[%d,%d]右:[%d,%d]);\n",x1, x2, c, a[i*2].l, a[i*2].r, a[i*2+1].l, a[i*2+1].r);
			insert(x1, mid, c, i*2);
			insert(mid, x2, c, i*2+1);

			if(oldColor != BLANK && oldColor != MIX)
			{
				insert(a[i].l, x1, oldColor, i*2);
				insert(x2, a[i].r, oldColor, i*2+1);
			}
		}
	}
}

int cnt[MAXN+2];
int done;			//统计的  ”前一段“颜色
void cal(int i)	//CALL:  cal(1);
{
	if(a[i].c == BLANK) { done=BLANK; return; }
	else if(a[i].c != MIX)	//纯色
	{
		if(done == BLANK || a[i].c != done)		//!③前一段颜色相同的话,不记录,而若前一段无色,可以记录
			cnt[a[i].c]++;
		done=a[i].c;
	}
	else	if(a[i].r-a[i].l >1)	//混合,要遍历子树,加 判断是否叶子
	{
		cal(i*2);
		cal(i*2+1);
	}
}
void print()
{
	for(int i=0; i<MAXN; i++)	//8000种颜色
	{
		if(cnt[i])
		{
			printf("%d %d\n", i, cnt[i]);
		}
	}
}

int n;
int main()
{
	init(0, 8000, 1);

	while(scanf("%d", &n) != EOF)
	{
		clear(1);
		memset(cnt, 0, sizeof(cnt));

		for(int i=0; i<n; i++)
		{
			int x1, x2, c;
			x1=Rint();	x2=Rint();	c=Rint();
			insert(x1, x2, c, 1);	//从顶端插入
		}

		done=BLANK;		//前一段 无色
		cal(1);
		print();
		printf("\n");
	}
}