题目链接: http://poj.org/problem?id=2528
题目大意,就是在大的区间贴海报,每次给一个小区间贴海报,问最后可见(一点点也算)的海报有几种。
线段树处理区间更新的做法是对的,每给定一个区间,然后成段更新,更新值为i,i为第i张海报,然后用一个vis数组标记出现的,就可以统计有多少个不同的海报了。
但是这题的区间比较大,直接处理会超时,于是学习了离散化的处理方法,其实不用想太多,就是把区间端点进行映射。
比如测试样例:
1 5 1 4 2 6 8 10 3 4 7 10端点:1 2 3 4 6 7 8 10
映射:1 2 3 4 5 6 7 8
这样在覆盖区间[1,8]时,我们就可以映射着覆盖[1,7]了。如果只有10000张海报,那么即使每个端点的值都不一样,最多也就20000个端点,每个端点做一个映射。覆盖的区间范围就大大缩小了。
只做这一个映射就可以ac了,但是是不严谨的,我也是看了讨论版才知道的。
举个例子
[1,10], [1, 4], [6,10]
1 4 6 10
1 2 3 4
即X[1] = 1, X[2] = 4, X[3] = 6, X[4] = 10
手工模拟覆盖会发现颜色少了一种,即5的海报颜色丢失了。
解决方案:给两个不连续点之间插入一个点,保证不出现原本不连续的区间被当成连续区间。
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include <queue>
using namespace std;
const int maxn = 40005; // 10000张海报、每张海报两个点×2 不连续点间插点×2
const int oo = 0xfffffff;
#define L_CHILD rt<<1, begin, mid
#define R_CHILD rt<<1|1, mid + 1, end
#define LL long long
int segTree[maxn<<2];
int n;
int vis[maxn];
int l[maxn], r[maxn], node[maxn<<2];
void pushDown(int rt) {
if(segTree[rt]) {
segTree[rt<<1] = segTree[rt<<1|1] = segTree[rt];
segTree[rt] = 0;
}
}
void update(int rt, int begin, int end, int left, int right, int val) {
if(left > end || right < begin) {
return ;
}
if(left <= begin && end <= right) {
segTree[rt] = val;
return ;
}
pushDown(rt);
int mid = begin + end >> 1;
update(L_CHILD, left, right, val);
update(R_CHILD, left, right, val);
}
int query(int rt, int begin, int end) {
if(segTree[rt]) {
if(!vis[segTree[rt]]) {
vis[segTree[rt]] = 1;
return 1;
}
return 0;
}
if(begin == end) {
return 0;
}
int mid = begin + end >> 1;
return query(L_CHILD) + query(R_CHILD);
}
int main() {
// freopen("test.txt", "w", stdout);
int t, n, x, y;
scanf("%d", &t);
while (t --) {
memset(vis, 0, sizeof(vis)), vis[0] = 1;
memset(segTree, 0, sizeof(segTree));
scanf("%d", &n);
int k = 1, m = 2;
for (int i = 1; i <= n; i ++) {
scanf("%d%d", l+i, r+i);
node[k++] = l[i];
node[k++] = r[i];
}
// 离散化后 去重
sort(node + 1, node + k);
for (int i = 2; i < k; i ++) {
if(node[i] != node[i-1]) {
node[m++] = node[i];
}
}
// 避免在出现[1,10], [1, 4], [6,10]
// 离散化: X[1] = 1, X[2] = 4, X[3] = 6, X[4] = 10
// 5的海报颜色丢失,解决:给两个不连续点之间插入一个点
for (int i = m - 1; i > 1; i --) {
if(node[i] != node[i-1] + 1) {
node[m++] = node[i-1] + 1;
}
}
sort(node + 1, node + m);
for (int i = 1; i <= n; i ++) {
int a = lower_bound(node + 1, node + m, l[i]) - node;
int b = lower_bound(node + 1, node + m, r[i]) - node;
update(1, 1, m-1, a, b, i);
}
printf("%d\n", query(1, 1, m));
}
return 0;
}
参考:
http://poj.org/showmessage?message_id=348360
http://www.zgxue.com/197/1977450.html