题意:给定一系列有先后顺序的数对[a,b],后面的数对可以覆盖前面的数对的部分或者全部,问最后可以看到多少个数对。
- 最简单的思路,建一hash数组,每输入一对[a,b],就对这一范围的数组的值作标记,看最后有多少标记,显然是会TLE的。使用线段树,其价值就在于我们不用每次都遍历[a,b]这么大范围的值,而只需要遍历线段树高度O(h)。
- 对于此题,线段树可以采用基本的染色原理,即对输入[a,b]在树上的区间染色,其中很重要的就是Lazy思路,虽然线段树的叶节点都是单个区间,若按最初的思路,需要对每个单个区间染色,而lazy就是直接对尽量大且是[a,b]子集的区间染色。
- 而当我们又要对区间染上另一种颜色时,lazy则必须改变了,我们不得不首先给区间的左右子区间染色相同颜色,与直接给它们的父区间染色是一致的,但是只有这样我们才能接下来给它的子区间染上输入的其它颜色,但是又不至于本身的颜色会消失。
- 离散化:若直接采用题目给出的区间1e7建立线段树,是会TLE,MLE的。而输入又说只有10000个区间输入,易知我们只关心区间的端点,而它们中间的范围是无关紧要的,所以可以采用离散化,将端点排序,使用排序后的位置作为输入区间。
此题离散化需要注意的是:前面的海报的中部本该能看到,但是离散化后的紧致性使其封闭,导致输出变小。
eg:
1
3
1 3
1 1
3 3
所以离散化时,当排序后的邻接两个实际距离相差大于1时,离散后的端点值在离散的基础上再加一。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define maxN 20005
#define maxS 70000
struct cmpNode
{
int sDis;
unsigned short segmentNo;
}sortSegment[maxN];
struct node
{
unsigned short a,b;
short posterNo;
}tree[maxS];
int sortSegmentNum;
int totPoster,num;
unsigned short segment[maxN];
char IsVisible[maxN];
int str2num(unsigned short sortSegmentNo) //读优化
{
int num = 0;
char c;
while((c = getchar())&&(c != ' ')&&(c != '\n')&&(c >= 0))
{
num *= 10;
num += c-'0';
}
sortSegment[sortSegmentNo].sDis = num;
sortSegment[sortSegmentNo].segmentNo = sortSegmentNo;
return 0;
}
int buildTree(int nodeNo,int a,int b)
{
int m = ((a+b)>>1);
tree[nodeNo].a = a;
tree[nodeNo].b = b;
if(a < b)
{
buildTree(nodeNo<<1,a,m); //左子树
buildTree((nodeNo<<1)+1,m+1,b); //右子树
}
return 0;
}
int posterInsert(int nodeNo,unsigned short s_a,unsigned short s_b,short posterNo)
{
int m = (tree[nodeNo].a+tree[nodeNo].b)>>1;
int left = (nodeNo<<1);
int right = (nodeNo<<1)+1;
if(tree[nodeNo].a == s_a&&tree[nodeNo].b == s_b)
tree[nodeNo].posterNo = posterNo;
else
{
if(tree[nodeNo].posterNo >= 0)
{
if(tree[nodeNo].posterNo > 0)
{
tree[left].posterNo = tree[nodeNo].posterNo; //前面着色时的lazy因为要更改颜色,必须向子树染色
tree[right].posterNo = tree[nodeNo].posterNo;
}
tree[nodeNo].posterNo = -1; //表明子树是多色的
}
if(s_a > m)
posterInsert(right,s_a,s_b,posterNo); //新颜色只可能插入右子树
else if(s_b <= m)
posterInsert(left,s_a,s_b,posterNo); //新颜色只可能插入左子树
else
{
posterInsert(left,s_a,m,posterNo);
posterInsert(right,m+1,s_b,posterNo);
}
}
return 0;
}
int checkVisible(int nodeNo)
{
if(tree[nodeNo].posterNo > 0) //下面的颜色已被覆盖,则不再往子树遍历
{
if(!IsVisible[tree[nodeNo].posterNo])
{
IsVisible[tree[nodeNo].posterNo] = 1;
num++;
}
}
else if(tree[nodeNo].posterNo == -1) //有子节点,即不能是叶节点并且没有子树被覆盖
{
checkVisible(nodeNo<<1);
checkVisible((nodeNo<<1)+1);
}
return num;
}
int cmp(const void* a,const void* b)
{
return (((cmpNode*)a)->sDis)-(((cmpNode*)b)->sDis);
}
int main()
{
int c,i;
scanf("%d",&c);
while(c--)
{
num = 0;
sortSegmentNum = 1;
memset(tree,0,sizeof(tree));
memset(IsVisible,0,sizeof(IsVisible));
scanf("%d",&totPoster);
getchar();
for(i = 0;i < totPoster;i++)
{
str2num(i*2);
str2num(i*2+1);
}
qsort(sortSegment,totPoster*2,sizeof(cmpNode),cmp); //离散化
segment[sortSegment[0].segmentNo] = sortSegmentNum; //距离从1开始
for(i = 1;i < totPoster*2;i++)
{
if(sortSegment[i].sDis-sortSegment[i-1].sDis > 0) //排序中不等的元素则新区间值加一
{
sortSegmentNum++;
if(sortSegment[i].sDis-sortSegment[i-1].sDis > 1) //露出海报可能的中间一段
sortSegmentNum++;
}
segment[sortSegment[i].segmentNo] = sortSegmentNum;
}
buildTree(1,1,sortSegmentNum);
for(i = 0;i < totPoster;i++)
posterInsert(1,segment[i*2],segment[i*2+1],i+1);
printf("%d\n",checkVisible(1));
}
return 0;
}