线段树习题,利用了延迟标记,与普通线段树频发查询不同,这里只进行了一次查询,所以事实上只是利用了线段树的结构和延迟标记的思想
几个注意点:
- 要注意给出的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
", iter->first, iter->second);
}
putchar('
');
}
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;
}