ZOJ - 1610 Count the Colors(线段树区间更新,单点查询)

时间:2023-03-10 04:31:52
ZOJ - 1610 Count the Colors(线段树区间更新,单点查询)

1、给了每条线段的颜色,存在颜色覆盖,求表面上能够看到的颜色种类以及每种颜色的段数。

2、线段树区间更新,单点查询。

但是有点细节,比如:

输入:

2

0 1 1

2 3 1

输出:

1 2

这种情况如果不处理,那么由于是检查点的颜色,会检查到0,1,2,3的颜色都为1,认为是一段连续的,就会输出 1 1

需要处理一下,代码中把所有的左端点都+1,避免了这种情况,比较巧妙。

3、

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std; #define L(root) ((root) << 1)
#define R(root) (((root) << 1) + 1) const int MAXN = ; struct st
{
// 区间范围
int left, right;
int flag;//-1没有颜色
} st[MAXN * ];
int color[MAXN];//每种颜色看到的段数
// 建树代码基本不变
void build(int root, int l, int r)
{
st[root].left = l, st[root].right = r, st[root].flag = -;
if (l == r)
{
return;
} int m = l + ((r - l) >> );
build(L(root), l, m);
build(R(root), m + , r);
} int query(int root, int x)//单点查询
{
if (st[root].left == st[root].right)
{
return st[root].flag;
} // 否则需将当前区间的“缓冲”值更新下去并修正各节点区间的总和
if (st[root].flag!=-)
{
st[L(root)].flag = st[root].flag;
st[R(root)].flag = st[root].flag;
st[root].flag = -;
} int m = st[root].left + ((st[root].right - st[root].left) >> );
if (x <= m)
{
return query(L(root), x);
}
else
{
return query(R(root), x);
}
} void update(int root, int l, int r, int v)//区间更新
{
// 如变更区间恰等于节点区间,只修正当前节点区间即可
if (st[root].left == l && st[root].right == r)
{
st[root].flag = v;
return;
} // 否则需向下修正相关节点区间
if (st[root].flag!=-)
{
st[L(root)].flag = st[root].flag;
st[R(root)].flag = st[root].flag;
st[root].flag = -;
} int m = st[root].left + ((st[root].right - st[root].left) >> );
if (r <= m)
{
update(L(root), l, r, v);
}
else if (l > m)
{
update(R(root), l, r, v);
}
else
{
update(L(root), l, m, v);
update(R(root), m + , r, v);
}
} int main()
{
int n,i;
int x1,x2,c;
int lastColor;//记录上一个颜色
int nowColor;//当前颜色
while(~scanf("%d",&n)){
build(,,);
for(i=;i<=n;++i){
scanf("%d%d%d",&x1,&x2,&c);
update(,++x1,x2,c);//++x1表示缩掉前面一点,处理了0 1 1,2 3 1这种情况,而且还符合了左端点从1开始
}
memset(color,,sizeof(color));
lastColor=-;
for(i=;i<=;++i){
nowColor=query(,i);
if(nowColor==lastColor)
continue;
else if(nowColor!=-)
++color[nowColor];
lastColor=nowColor;
}
for(i=;i<=;++i)//颜色标号是0~8000
if(color[i])
printf("%d %d\n",i,color[i]);
printf("\n");
}
return ;
}

c2.这个没有用上面的左端点+1,但是在建树的时候是用的[0,1],[1,2],[2,3],类似这样的建树,比较刁

上面的建树是[0,1],[2,3],类似这样的,还是有点区别的。具体看代码吧。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=;
struct Node
{
int l,r;
int color;
}segTree[MAXN*];
int color[MAXN];
int temp;
void Build(int i,int l,int r)
{
segTree[i].l=l;
segTree[i].r=r;
segTree[i].color=-;//-1表示没有颜色
if(l+==r)return;
int mid=((l+r)>>);
Build(i<<,l,mid);
Build((i<<)|,mid,r);
}
void insert(int i,int l,int r,int c)
{
if(l==r)return;
if(segTree[i].color==c)return;
if(l<=segTree[i].l&&r>=segTree[i].r)
{
segTree[i].color=c;
return;
}
if(segTree[i].color>=)//存在颜色,往下更新
{
segTree[i<<].color=segTree[i].color;
segTree[(i<<)|].color=segTree[i].color;
segTree[i].color=-;//表示有多种颜色
}
int mid=((segTree[i].l+segTree[i].r)>>);
if(r<=mid) insert(i<<,l,r,c);
else if(l>=mid) insert((i<<)|,l,r,c);
else
{
insert(i<<,l,mid,c);
insert((i<<)|,mid,r,c);
}
segTree[i].color=-;
}
void Count(int i)//统计各颜色的段数
{
if(segTree[i].color==-)
{
temp=-;
return;
}
if(segTree[i].color!=-)
{
if(segTree[i].color!=temp)//temp存的是前一段的颜色
{
color[segTree[i].color]++;
temp=segTree[i].color;
}
return;
}
if(segTree[i].l+!=segTree[i].r)
{
Count(i<<);
Count((i<<)|);
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,a,b,c;
int Max;
while(scanf("%d",&n)!=EOF)
{
Build(,,);
Max=;
while(n--)
{
scanf("%d%d%d",&a,&b,&c);
insert(,a,b,c);
if(c>Max)Max=c;
}
temp=-;
memset(color,,sizeof(color));
Count();
for(int i=;i<=Max;i++)
{
if(color[i])printf("%d %d\n",i,color[i]);
}
printf("\n");
}
return ;
}