您的位置:首页 > 其它

POJ 2528 线段树+离散化(水水的线段树+略复杂的离散化)

2015-08-20 20:43 393 查看
题意:有一条数轴[1,1QW],给定m(最大1W)个区间,相当于给每个区间染色,每一次都是使用的不同的颜色,问最后能在整个数轴上看到多少颜色。

INPUT:第一行数据组数,每组数据第一行m表示有m个区间,之后的m行每行两个数,表示区间。

大家都说这是一道线段树的入门题,水题,却卡了我很长时间。

我一开始还在思考,这题要在线段树节点中附加多少信息啊,一开始思考各种天花乱坠,代码也因为附加信息的增加越写越长,因为我一直想的是最后输出的答案就是线段树第一个节点,所以搞得附加信息越来越多。

不过根本不用那么麻烦,这道题之所以说是线段树的入门题,是因为它甚至连一个附加信息都没有,只需要一个lazy-tag,所以说是入门题,代码的线段树部分也是非常的好写。不过理解起来有点麻烦,我就是在理解这颗线段树时上碰到了难处。

这道题利用的只是线段树的区间修改,所以有一个pushdown函数。

为了表述方便,暂且说lazy-tag是一个需要在pushdown过程中重置为0的,附加信息是lazy-tag的对象,不需要在pushdown后重置为0。因为这道题与线段树有关的数组只有一个,所以我纠结它到底是lazy-tag还是附加信息,当发现吧pushdown中的col[p] = 0去掉后输出就不对了,思考许久,是这样的,因为col[p]表示的是p节点代表的区间是col[p]这个颜色,在我们查询时,是直接查询到底的,而且查询的过程中还要不断pushdown,如果在之前的pushdown中不把col[p]置为0,那么它会产生影响,可以理解为后效性。这一整段是在说pushdown函数中简简单单的一句col[p] = 0,存在的意义的,可能你们都懂,跳过这一段那就好了,我在这个的理解上花了一段时间。

然而这道题还需要离散化,普通的离散化是不对的,看了众神犇的做法:要往两个间距大于1的点中再插入一个点,不过这个点在从排序后的数组转化为离散后的区间时是没用的。如何证明普通的离散是不对的,我也只能给出一个例子,如三张海报为:1~10 1~4 6~10,普通离散化之后为1~4 1~2 3~4,就出错了。

总结地来说一遍:

首先离散化,需要注意细节,否则离散化的结果很容易出错,要区分开这个点是左端点还是右端点,我们可以用负数表示其中的一个;

之后把每个区间添加到线段树(注意离散化的时候不要打乱区间的顺序),只需要传递一个lazy-tag,表示当前区间是哪个海报即可。查询时要查询到线段树的叶子节点,就是遍历所有节点,把出现过的海报记录下来,输出个数即可。

就我理解,这样解的本质还是维护这个数轴,每次都是把一个区间的所有点改成一个数,最后遍历这个数轴,数出有多少不同的数。这个数的过程和我们的实际做法差不多,但是这个本质当然会T,所以我们就用了线段树优化了区间修改这一步,所以也说,这颗线段树只需要一个lazy-tag。

线段树也是一个很灵活的东西。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define M 22222
using namespace std;

int N, a, b, k, ans;
int l[M], r[M], jud[M];
int col[M<<4];

struct node{
int num, key;
}t[M<<3];
bool cmp(node a, node b){ return a.key < b.key;}

void pushdown(int p){
if(col[p]){
int lc = p << 1, rc = p << 1 | 1;
col[lc] = col[rc] = col[p];
}
col[p] = 0;
}

void update(int p, int l, int r){
if(l >= a && r <= b){col[p] = k; return;}
int lc = p << 1, rc = p << 1 | 1, mid = (l+r) >> 1;
pushdown(p);
if(a <= mid) update(lc, l, mid);
if(b > mid) update(rc, mid+1, r);
}

void query(int p, int l, int r){
if(col[p]){
if(!jud[col[p]]) ans++;
jud[col[p]] = 1;
return;
}
pushdown(p);
query(p<<1, l, (l+r)>>1);
query(p<<1|1, (l+r)>>1|1, r);
}

int main()
{
scanf("%d", &N);
while(N--){
memset(col, 0, sizeof col);
memset(jud, 0, sizeof jud);
memset(t, 0, sizeof t);
int m, cnt = 0, tot = 1;
ans = 0;
scanf("%d", &m);
for(int i = 1; i <= m; i++){
scanf("%d %d", l+i, r+i);
t[++cnt].num = -i;
t[cnt].key = l[i];
t[++cnt].num = i;
t[cnt].key = r[i];
}
sort(t+1, t+cnt+1, cmp);
for(int i = cnt; i > 1; i--)
if(t[i].key - t[i-1].key > 1) t[++cnt].key = t[i].key - 1;
sort(t+1, t+cnt+1, cmp);
l[-t[1].num] = 1;
for(int i = 2; i <= cnt; i++){
if(t[i].key != t[i-1].key) tot++;
if(t[i].num > 0)  r[t[i].num] = tot;
else l[-t[i].num] = tot;
}
for(int i = 1; i <= m; i++){
a = l[i], b = r[i], k = i;
update(1, 1, tot);
}
query(1, 1, tot);
printf("%d\n", ans);
}
}


做了这道题,真是深深地加深了我对离散化的理解!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: