D - Mayor's posters
题意:
在宽度为10000000的墙壁上,按顺序贴n张海报(1 <= n <= 10000),每种海报的颜色不同,颜色用数字表示,且第 i 张海报的颜色就是 i ,给出海报的左右坐标。之后贴上的海报可以覆盖之前贴上的海报。问:在贴完所有n张海报之后,能从墙上看到几种颜色。
输入:
先输入一个T(1),表示测试样例的数量 接着输入一个n(5),表示海报的数量 接下来的n(5)行,每行代表一张海报,包括两个整数,分别代表海报的左端点与右端点。 1 5 1 4 2 6 8 10 3 4 7 10
输出:
4
思路:
因为刚刚学过线段树的区间更新,所以刚刚看到题目,觉得这道题用线段树是可以写的。但是又发现墙壁的长度为10000000。显然,直接用这样的长度建树不仅内存需要巨大,而且极易超时。
所以需要用离散来处理一下。因为海报数量是小于10000的,海报有左右两个坐标点,所以说本题最多只有20000个点。此处的离散比较简单。意思是用小的数来替换大的数,例如一些点的坐标为:100、1000、1111、9999。我们可以用1来代替100,2来代替1000,3来代替1111,4来代替9999。这样一来,无论在还未离散时,坐标能达到10000000,但在离散后,坐标最大只能达到20000了。
离散过程如下:
struct Poster { int x; int id; }post[maxn * 4]; int cmp1(Poster a , Poster b) { return a.x < b.x; } sort (post + 1 , post + n * 2 + 1 , cmp1); int last = 0; int ans = 0; for (int i = 1 ; i <= n * 2 ; i++) { if (post[i].x == last) post[i].x = ans; else { last = post[i].x; post[i].x = ++ans; } }
在代码中,我用结构体Poster类的post [ 20000 ] 数组来储存点的信息,x代表点的坐标,id代表点属于的海报的颜色。我们首先需要把所有点按坐标,从小到大排序。接着我们定义一个last用于存前一个点在离散之前的坐标,再定义一个ans用来做点的新坐标,如果前后两个点在未离散时的坐标相同,则前后两个点的新坐标相同。如果前后两个点在未离散时的坐标不相同,则后面点的新坐标比前面点的新坐标大1。这样一来我们就完成了离散。
离散过后。我们需要开始建树,然后用区间更新的方法把海报的颜色更新到树上。
此处我想要将海报从后往前地进行更新。更新时,如果发现此处已经有颜色,那么就不进行更新。因为后面的海报是会覆盖前面的海报的。如果这张海报能够有地方上色,说明在最后,我们是能够看到这张海报的,此时我们用一个flag来进行标记,如果上色,flag = 1,如果flag = 1 ,那么我们最后的答案 result ++ 。所以我们需要做下面几件事:
- 先对离散后的点按照从 id 大到 id 小的顺序排序。
- 建树
- 用海报对树更新,同时计算result
- 输出答案
代码如下:
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 5;
struct Poster
{
int x;
int id;
}post[maxn * 4];
int flag;
int cmp1(Poster a , Poster b)
{
return a.x < b.x;
}
int cmp2(Poster a , Poster b)
{
if (a.id == b.id)
return a.x < b.x;
return a.id > b.id;
}
struct Tree
{
int lson;
int rson;
bool vis;
}tree[maxn * 4 ];
void Build (int l , int r , int h)
{
tree[h].lson = l;
tree[h].rson = r;
tree[h].vis = 0;
if (l == r)
return ;
int mid = (l + r) >> 1;
Build (l , mid , h << 1);
Build ( mid + 1 , r , h << 1 | 1);
}
void Push_Up(int h)
{
tree[h].vis = tree[h << 1].vis && tree[h << 1 | 1].vis;
}
void Query (int l , int r , int h)
{
if (tree[h].vis)
return ;
if (l == tree[h].lson && r == tree[h].rson)
{
flag = 1;
tree[h].vis = 1;
return;
}
int mid = (tree[h].lson + tree[h].rson) >> 1;
if (l > mid)
Query (l , r , h << 1 | 1);
else if (r <= mid)
Query (l , r , h << 1);
else
{
Query (l , mid , h << 1 );
Query (mid + 1 , r , h << 1 | 1);
}
Push_Up(h);
}
int main ()
{
int T;
scanf ("%d" , &T);
while (T--)
{
int n ;
scanf("%d" , &n);
for (int i = 1 ; i <= n * 2 - 1 ; i+=2)
{
scanf ("%d%d" , &post[i].x , &post[i + 1].x);
post[i].id = post[i + 1].id = i;
}
sort (post + 1 , post + n * 2 + 1 , cmp1);
int last = 0;
int ans = 0;
for (int i = 1 ; i <= n * 2 ; i++)
{
if (post[i].x == last)
post[i].x = ans;
else
{
last = post[i].x;
post[i].x = ++ans;
}
}
Build (1 , n * 2 , 1);
sort (post + 1 , post + n * 2 + 1 , cmp2);
int result = 0;
for (int i = 1 ; i <= n * 2 - 1 ; i+=2)
{
flag = 0;
Query (post[i].x , post[i + 1].x , 1);
if (flag) result++;
}
printf ("%d\n" , result);
}
return 0;
}