1 5 1 4 2 6 8 10 3 4 7 10Sample Output
4
题意:有一面墙,被等分为1QW份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到。)
思路:线段树区间更新+离散化
----------------------------------->>>>>>离散化<<<<<<----------------------------------------
原来的:1 2 3 4 6 7 8 10 (去重后排序)
映射后:1 2 3 4 5 6 7 8
原来的:[1, 4] [2, 6] [8, 10] [3, 4] [7, 10]
映射后:[1, 4] [2, 5] [7, 8] [3, 4] [6, 8]
很多代码能够AC但是不正确,比如下面的这组数据:
离散化后前:[1, 10] [1, 3] [6, 10]
离散化后是:[1, 4] [1, 2] [3, 4]
离散化前,3与6之间是有间隙的,但是离散化后,2与3相连的,于是原来3与6之间的部分就看不到,少算了,得到的结果是2,而正解是3。
为了解决这个问题,我们可以对排序后的数组处理一下。比如[1, 3, 6, 10]如果相邻数字间距大于1,就在其中加上任意一个数字,比如加成[1, 3, 4, 6, 7, 10, 11],这样处理就好了。
#include
#include
#include
#include
using namespace std;
#define N 11111
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define abbreviations int l,int r,int rt
#define mid (l+r)>>1
int le[N],ri[N],root[N<<4],lr[N<<4],m,cnt;
bool flag[N<<4];
void update(abbreviations, int L,int R,int c)
{
if(L <= l && R >= r)
{
root[rt] = c;
return ;
}
if(root[rt] != -1)
{
root[rt<<1] = root[rt<<1|1] = root[rt];
root[rt] = -1;
}
int m = mid;
if(m >= L) update(lson,L,R,c);
if(m < R) update(rson,L,R,c);
}
void query(abbreviations)
{
if(root[rt] != -1)
{
if(!flag[root[rt]]) cnt++;
flag[root[rt]] = true;
return ;
}
if(l == r)
return ;
int m = mid;
query(lson);
query(rson);
}
int main()
{
int t,n,i,nn;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i = 0,nn = 0; i < n; i++)
{
scanf("%d%d",&le[i],&ri[i]);
lr[nn++] = le[i]; //记录左端点
lr[nn++] = ri[i]; //记录右端点
}
sort(lr,lr+nn); //排序不能少
m = unique(lr,lr+nn)-lr; //去重
for(i = m-1; i >= 0; i--)
{
if(lr[i] != (lr[i-1]+1)) lr[m++] = lr[i] + 1; //相邻数字大于1就添加一个数
}
sort(lr,lr+m); //添加数字后不要忘记排序
memset(root,-1,sizeof(root)); //建树叶子节点全部赋值为-1
for(i = 0; i < n; i++)
{
int L = lower_bound(lr, lr+m, le[i]) - lr; //二分查找左端点
int R = lower_bound(lr, lr+m, ri[i]) - lr; //二分查找右端点
update(0, m, 1, L, R, i); //更新
}
memset(flag,false,sizeof(flag));
cnt = 0;
query(0, m, 1); //求出答案
printf("%d\n",cnt);
}
return 0;
}