题意:有一面墙,被等分为1千万份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1千万。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?看见一部分也算看到。
解法:线段树+离散化
有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。
现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9
然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9
对其升序排序,得2 3 4 6 8 9 10
然后建立映射
2 3 4 6 8 9 10
↓ ↓ ↓ ↓ ↓ ↓ ↓
1 2 3 4 5 6 7
那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,且结果一致。代码如下
#include<stdio.h>
struct node
{
int aa,bb,left,right,flag,color;
}c[100000];
int b[50000],a[50000][3],f[50000],tot;
void build(int x,int y)
{
int now;
tot++;
now=tot;
c[now].aa=x;
c[now].bb=y;
c[now].flag=0;
c[now].color=0;
if(x!=y)
{
c[now].left=tot+1;
build(x,(x+y)/2);
c[now].right=tot+1;
build((x+y)/2+1,y);
}
}
void insert(int x,int y,int z,int xg)
{
int mid;
if(x<=b[c[z].aa]&&y>=b[c[z].bb])
{
c[z].flag=1;
c[z].color=xg;
}
else
{
if (c[z].flag==1)
{
c[c[z].right].color=c[c[z].left].color=c[z].color;
c[c[z].left].flag=c[c[z].right].flag=1;
c[z].flag=0;
}
mid=(c[z].aa+c[z].bb)/2;
if(x<=b[mid])
insert(x,y,c[z].left,xg);
if(y>b[mid])
insert(x,y,c[z].right,xg);
}
}
void find(int x)
{
if(c[x].flag==1)
f[c[x].color]=1;
else if(c[x].aa!=c[x].bb)
{
find(c[x].left);
find(c[x].right);
}
}
void dog(int x,int y)
{
int x1,y1,mid,tp;
x1=x;
y1=y;
mid=b[(x+y)/2];
do
{
while(b[x]<mid)
x++;
while(b[y]>mid)
y--;
if (x<=y)
{
tp=b[x];
b[x]=b[y];
b[y]=tp;
x++;
y--;
}
}while(x<y);
if(x<y1)
dog(x,y1);
if(x1<y)
dog(x1,y);
}
void main()
{
int i,g,tx,t,n,j;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
scanf("%d",&n);
g=0;
for(j=1;j<=n;j++)
{
scanf("%d%d",&a[j][0],&a[j][1]);
a[j][2]=j;
b[++g]=a[j][0];
b[++g]=a[j][1];
}
dog(1,2*n);
tot=0;
build(1,2*n);
for(j=1;j<=n;j++)
insert(a[j][0],a[j][1],1,a[j][2]);
for(j=1;j<=n;j++)
f[j]=0;
find(1);
tx=0;
for(j=1;j<=n;j++)
if(f[j]==1)
tx++;
printf("%d\n",tx);
}
}