看完线段树本来打算找这道基础题练手,但是悲惨的砸进去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"); } }