题意 : 在墙上贴海报, n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000)。求出最后还能看见多少张海报。
分析 : 很容易想到利用线段树来成段置换,最后统计总区间不同数的个数。但是这里有一个问题,就是区间可以很大,线段树开不了那么大的空间,遂想能不能离散化。实际上只记录坐标的相对大小进行离散化最后是不影响我们计算的,但是光是普通的离散化是不行的,就是我们贴海报的实际意义是对(l, r)段进行添加,而不是对于这个区间的点进行添加,是段树不是点树,如果这样普通离散化的话就会出现一个问题,比如数据1-10、1-4、6-10 行的,我们离散化后是1-4、1-2、3-4 ,不离散的结果是 3 但是离散化后再计算就是 2 了!原因就是我们是成段更新而不是点更新,进行添加海报的(l, r)的意义是对这一段进行添加,而离散化之后将原本不相邻的点变成了相邻的点,就导致了上面例子 4 - 6被覆盖了!解决这个问题的方法就是,在离散化的时候,将原本不相邻的两个点中间添加一个数,来表示中间是有“缝隙”的。
另外说一下 : POJ上的数据弱了,所以可能离散化的时候没有注意这个问题也能AC,可以去discuss板块里面看看大牛们的数据
#include<stdio.h> #include<string.h> #include<algorithm> #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 using namespace std; const int maxn = 21112; struct Interval{ int L, R; }; Interval Sec[maxn>>1]; int arr[maxn<<2]; int col[maxn<<3]; bool vis[maxn]; int ans; void PushDown(int rt) { if (col[rt]) { col[rt<<1] = col[rt<<1|1] = col[rt]; col[rt] = 0; } } void query(int l, int r, int rt) { if(col[rt]){//下面的这一段都是 col[rt] 了, 所以不必再往下更新 if(!vis[col[rt]]) ans++; vis[col[rt]] = true; return ; } if(l == r) return; int m = (l + r)>>1; query(lson); query(rson); } void update(int L,int R,int c,int l,int r,int rt) { if (L <= l && r <= R) { col[rt] = c; return ; } PushDown(rt); int m = (l + r) >> 1; if (L <= m) update(L , R , c , lson); if (R > m) update(L , R , c , rson); } int main(void) { int nCase; scanf("%d", &nCase); while(nCase--){ int n, top = 0; memset(col, 0, sizeof(col)); memset(vis, false, sizeof(vis)); scanf("%d", &n); for(int i=0; i<n; i++){ scanf("%d %d", &Sec[i].L, &Sec[i].R); arr[top++] = Sec[i].L, //记录所有出现的点 arr[top++] = Sec[i].R; } sort(arr, arr+top); top = unique(arr, arr+top) - arr;//将点去重 for(int i=top-1; i>0; i--){ if(arr[i] != arr[i- 1] + 1)//在原本不相邻的点中间添加一个数,因为后面是要sort的,所以添加在数组末尾即可 arr[top++] = arr[i-1] + 1;//要做到这个操作只要从后往前判断就可以了,或者一开始把top的值记下来也行 } sort(arr, arr+top); ans = 0; for(int i=0; i<n; i++){ int l = lower_bound(arr, arr+top, Sec[i].L) - arr;//在离散化之后的坐标系arr中寻找左端点 int r = lower_bound(arr, arr+top, Sec[i].R) - arr;//寻找右端点 update(l+1, r+1, i+1, 1, top, 1);//更新,注意离散化之后的坐标是从0开始的,这里我们把坐标人为+1进行计算 } query(1, top, 1);//查询整个区间统计不同数的个数 printf("%d ", ans); } return 0; }
瞎 :对于线段树模版不能硬套,要结合题目对于模版进行适当的修改才能更好发挥这个数据结构的威力!例如这题的查询操作,在查询的时候提前判断下面是否都是一样的数了这个操作,如果死套模版,可能就是单点查询存数组再去unique判断,这个是很不明智的!