题目链接:poj2528
题意:
n张等高的海报,贴在一堵无限长度的墙上,给出海报的起始坐标,海报张贴的顺序,海报会被覆盖,问最后能看见多少不同的海报。
思路:
海报的下标最大为1000W,线段树是存不下的,但是海报的数量只有10000,可以利用离散化技巧,把这10000个海报的起始下标重新分配序号,再用线段树维护。
这里是区间更新,整个区间置为一个值(其实只需要维护一个懒标记就可以了)。查询的时候,要跑遍整棵树上被打了标记的点,用一个vis数组判断这个颜色是否已经被访问过。
小结
想起去年离散化不怎么理解,现在很熟练就写出来了。。。但是这题依旧WA了我n发。。。看了无数遍没有找出原因,最后没想到是push down的时候,没有判断当前节点是否打了懒标记就往下push。。。回去把第一发WA的改了也过了。。。
PS
这题数据比较水。离散化的时候当两个点之间差距大于1时,要往里面加一个点。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 40000 + 10;
struct Node
{
int le, ri;
Node(){}
Node(int le, int ri):le(le), ri(ri){}
}A[maxn];
bool vis[maxn];
int color[maxn<<2];
int v[maxn];
void pushdown(int rt){
if(color[rt]!=-1){
color[rt<<1] = color[rt];
color[rt<<1|1] = color[rt];
color[rt] = -1;
}
}
void Set(int rt, int a, int b, int l, int r, int x){
if(a<=l && r<=b){
color[rt] = x;
}
else{
int mid = (l+r)>>1;
pushdown(rt);
if(a<=mid) Set(rt<<1, a, b, l, mid, x);
if(mid<b) Set(rt<<1|1, a, b, mid+1, r, x);
}
}
int query(int rt, int l, int r){
if(color[rt]==-1){
int mid = (l+r)>>1;
if(l==r)
return 0;
else
return query(rt<<1, l, mid) + query(rt<<1|1, mid+1, r);
}
else{
if(vis[color[rt]])
return 0;
else{
vis[color[rt]] = 1;
return 1;
}
}
}
int main(){
int t, n;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
int cnt = 0;
memset(vis, 0, sizeof(vis));
memset(color, -1, sizeof(color));
for(int i=0; i<n; ++i){
scanf("%d%d", &A[i].le, &A[i].ri);
v[cnt++] = A[i].le;
v[cnt++] = A[i].ri;
}
sort(v, v+cnt);
cnt = unique(v, v+cnt) - v;
int tmp_cnt = cnt;
for(int i=1; i<cnt; ++i){
if(v[i] - v[i-1] > 1) v[tmp_cnt++] = v[i-1] + 1;
}
sort(v, v+tmp_cnt);
cnt = unique(v, v+tmp_cnt) - v;
for(int i=0; i<n; ++i){
int le = lower_bound(v, v+cnt, A[i].le) - v + 1;
int ri = lower_bound(v, v+cnt, A[i].ri) - v + 1;
Set(1, le, ri, 1, cnt, i+1);
}
printf("%d\n", query(1, 1, cnt));
}
return 0;
}