Mayor’s posters
题目大意是给你一个区间[1,10000000],然后有n个子区间,按照顺序填到[1,10000000]上,问你最后裸露在外的区间个数是几个。要注意的是这里的区间的端点不是一个点,而是单位长度,也就是说
[1,2]和[3,4]能够完全覆盖[1,4]。
做法是首先记录n个区间的左右端点,将其记录到point[]上(重复的去掉)。
然后我们要进行离散化。为什么呢?
因为在两个区间之间有许多空隙,比如[1000,2000]和[9000,10000],这2001到8999的段我们是不需要的,那么我们可以相对的做一个映射如下:
1000->1
2000->2
9000->3
10000->4
这样就可以把区间的范围缩小,减少运算量。
但是,只是这样普通地离散化是不够的
假设一种情况,
2
3
1 10
1 4
5 10
3
1 10
1 4
8 10
第一个case和第二个case映射完后都是1,2,3,4,但是显然第一个case的解是2,第二个case的解是3。问题就出在[1,4]和[5,10]的两个区间,4和5是连续的,而[1,4]和[8,10],4和8不是连续的,
我们在离散化的时候没有模拟出这种情况,如果原来的例子是1,4,7,8,10,(7是额外加的),那么我们可以在第二个例子里面,映射成1,2,3,4,5,区间还是[1,2]和[4,5],就解决了这个问题。为了方便,我们在1,4和8,10之间也加上3和9,这样写起代码来比较方便。
AC代码如下(C++编译器,G++没试过):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10005;
struct node
{
int l, r, lz;
} tr[16 * maxn];
int left[maxn], right[maxn], point[4 * maxn], vis[4 * maxn];
int cnt;
void lazy(int d)
{
if(tr[d].lz != 0)
{
int ld = 2 * d;
int rd = 2 * d + 1;
tr[ld].lz = tr[d].lz;
tr[rd].lz = tr[d].lz;
tr[d].lz = 0;
}
}
void build(int d, int l,int r)
{
tr[d].l = l, tr[d].r = r;
if(tr[d].l == tr[d].r)
{
tr[d].lz = 0;
return ;
}
int mid = (l + r) / 2;
int ld = 2 * d;
int rd = 2 * d + 1;
build(ld, l, mid);
build(rd, mid + 1, r);
}
void update(int d, int l, int r, int val)
{
if(tr[d].l == l && tr[d].r == r)
{
tr[d].lz = val;
return;
}
lazy(d);
int mid = (tr[d].l + tr[d].r) / 2;
int ld = 2 * d;
int rd = 2 * d + 1;
if(l > mid) update(rd, l, r, val);
else if(r <= mid) update(ld, l, r, val);
else
{
update(ld, l, mid, val);
update(rd, mid + 1, r, val);
}
}
void query(int d, int l, int r)
{
if(tr[d].lz!= 0)
{
if(vis[tr[d].lz] == 0)
{
cnt++;
vis[tr[d].lz] = 1;
}
tr[d].lz = 0;
return ;
}
if(tr[d].l == tr[d].r)
{
return;
}
lazy(d);
int mid = (tr[d].l + tr[d].r) / 2;
int ld = 2 * d;
int rd = 2 * d + 1;
if(r <= mid) query(ld, l, r);
else if(l > mid) query(rd, l, r);
else
{
query(ld, l, mid);
query(rd, mid + 1, r);
}
}
int main()
{
int cas;
scanf("%d", &cas);
while(cas --)
{
int n, np = 0;
cnt = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &left[i], &right[i]);
np ++;
point[np] = left[i];
np ++;
point[np] = right[i];
}
sort(point + 1, point + np + 1);
int mp = 1;
for(int i = 2; i <= np; i++)
{
if(point[i] != point[i-1])
{
mp ++;
point[mp] = point[i];
}
}
for(int i = mp; i >= 2; i--)
{
if(point[i] != point[i-1] + 1)
{
mp ++;
point[mp] = point[i-1] + 1;
}
}
sort(point + 1, point + 1 + mp);
memset(tr, 0, sizeof(tr));
memset(vis, 0, sizeof(vis));
build(1, 1, mp);
for(int i = 1; i <= n; i++)
{
int l = lower_bound(point + 1, point + mp + 1, left[i]) - point;
int r = lower_bound(point + 1, point + mp + 1, right[i]) - point;
update(1, l, r, i);
}
query(1, 1, mp);
printf("%d\n", cnt);
}
}