Problem:acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1610
Reference:blog.youkuaiyun.com/shuangde800/article/details/8186332
分析:线段树成段更新,错在两个地方。
第一点,题中给出的边界是线段的两个端点,也就是说每个标号表示一个点而不是一个小线段。
于是我采用标号表示它右边的一段,所以输入“x1 x2 c”,我处理成:update(x1, x2-1, c, ...)。
第二点,连在一起的两段同色段要合并成一大段,但不能简单写个 pushup() 来完成,因为两个同色段可能分在两个结点所管的区间内,但又没有结点所管的区间那么长,所以会出现漏合并的情况。于是多开一个 c 数组,在 query 的时侯,把更新完的最后结果都放进这个数组中,然后再用一个循环找颜色段。
Source Code
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 8000;
int tree[N+5<<2];
int cnt[N+1]; // cnt[i]表示第i种色的段数
int c[N]; // c[i]表示第i段的颜色(0<=i<N)
/*
void pushup(int x)
{
if(~tree[x<<1] && tree[x<<1] == tree[x<<1|1])
tree[x] = tree[x<<1];
}
*/
void pushdown(int x)
{
if(~tree[x])
{
tree[x<<1] = tree[x<<1|1] = tree[x];
tree[x] = -1;
}
}
void update(int ul, int ur, int v, int l, int r, int id)
{
if(ul <= l && r <= ur)
{
tree[id] = v;
return;
}
pushdown(id);
int m = l + r >> 1;
if(ul <= m)
update(ul, ur, v, l, m, id<<1);
if(ur > m)
update(ul, ur, v, m+1, r, id<<1|1);
// pushup(id);
}
void query(int l, int r, int id)
{
if(~tree[id])
{
// ++cnt[tree[id]];
for(int i=l,col=tree[id]; i<=r; ++i)
c[i] = col;
return;
}
if(l == r) return;
int m = l + r >> 1;
query(l, m, id<<1);
query(m+1, r, id<<1|1);
}
int main()
{
int n;
while(~scanf("%d", &n))
{
memset(tree, -1, sizeof tree);
for(int a,b,v; n--; )
{
scanf("%d%d%d", &a, &b, &v);
update(a, b-1, v, 0, N-1, 1);
}
memset(c, -1, sizeof c);
query(0, N-1, 1);
memset(cnt, 0, sizeof cnt);
for(int i=0; i<N; ++i)
{
if(c[i] == -1) continue;
++cnt[c[i]];
for(int j=i+1; j<N && c[j]==c[i]; ++j)
i = j;
}
for(int i=0; i<=N; ++i)
if(cnt[i])
printf("%d %d\n", i, cnt[i]);
putchar('\n');
}
return 0;
}
注释掉的就是之前用 pushup() 的错误代码