标准解法是用的离散化。此题的特殊性在于普通离散化后,两个相邻的值之间的区间也可能是有效的区间。例如:
有三张海报[1, 4] [5, 6][1, 6],离散化后1->1, 2->4, 3->5, 4->6。他们就会覆盖[1, 6]这张海报。
另外三张海报[1, 3] [5, 6][1, 6],离散化后1->1, 2->3, 3->5, 4->6。他们不会覆盖[1, 6]这张海报。
而此时两组数据的前两张海报都会变成[1, 2] [3, 4] 两个区间,都覆盖了[1,4]区间。
解决方案是如果发现两个离散值之间的距离大于1,就在两个值之间插入一个数字。例如第二组中的3和5,他们大于1,就插入一个数,为了保证最后用二分查找的方便,插入(3 , 5)之间的任意数字都可以。
另外最开始的时候,我采用的动态生成数组存储离散化的值,最后导致了段错误(SIGSEGV)。采取使用静态数组,并且把部分的数组移到main函数外面,就可以了。
结果是:4476kB50ms
可以看出,不管是时间复杂度还是空间复杂度都比插入节点建树的方案好,而且自己实现的quicksort比系统的快,只是编码量比较大。
#include <stdio.h>
#include <string.h>
//#include <stdlib.h>//qsort头文件
//#include <algorithm>//sort头文件
#include <iostream>
using namespace std;
struct Poster{
int l,u;
};
struct InTree{
int l,u;
InTree *left,*right;
int color;//-2表示该区间是混合色或者还未涂色
InTree(int low,int upp){
l= low; u = upp;
left = right = 0;
color = -2;
}
};
InTree* Build(int low, int upp){
InTree *root = new InTree(low,upp);
if(low < upp){
//递归构建左右子树,并初始化max和min值
root->left = Build(low,(low+upp)/2);
root->right = Build((low+upp)/2+1,upp);
}
return root;
}
void Update(InTree *root,int low,int upp,int c){
if(low <= root->l && root->u <=upp){
root->color = c;
return;
}
else{
if(root->color >= 0){//当本区间是单一色时,需要将颜色下推
root->right->color = root->left->color = root->color;
root->color = -2;
}
if(low <= (root->l+root->u)/2){
Update(root->left,low,upp,c);
}
if(upp >= (root->l+root->u)/2 + 1){
Update(root->right,low,upp,c);
}
}
}
void Query(InTree *root,bool *map){
//查询root子树中全涂成一种颜色的区间,并将区间的颜色对应的map位置置为true
if(root == 0)
return;
else if(root->color >=0)
map[root->color] = true;
else{
Query(root->left,map);
Query(root->right,map);
}
}
void Delete(InTree *root){
//递归删除节点
if(root == 0)
return;
else{
Delete(root->left);
Delete(root->right);
delete root;
}
}
int CMP(const void *a,const void *b){
int* ca = (int *)a;
int* cb = (int *)b;
if(*ca > *cb)return 1;
if(*ca < *cb)return -1;
if(*ca==*cb)return 0;
}
int BinarySearch(int *a,int n,int key){
int l = 1,u = n;
while(l<=u){
int mid = l+(u-l)/2;
if(a[mid] == key)
return mid;
else if(a[mid]<key)l = mid +1;
else u = mid - 1;
}
return -1;
}
int Partition(int *pos,int n){
int p = pos[0];
int i=0,j=n;
while(true){
while(pos[++i]< p && i<n-1);
while(pos[--j]> p);
if(i>=j)break;
else{
int t = pos[i];
pos[i] = pos[j];
pos[j] = t;
}
}
pos[0] = pos[j];
pos[j] = p;
return j;
}
void Quicksort(int *pos,int n){
if(n>1){
int i = Partition(pos,n);
Quicksort(pos,i);
Quicksort(pos+i+1,n-i-1);
}
}
//如果把这两个数组写在main函数中,会出现rte的错误。估计是代码段容量不够。同理如果都写成new形式,也会容量不够
int pos[10010*2];
int indexs[10010*3];//最多3*n个位置(用index做名字会出错)
int main(){
int c;
int n;
//int pos[10010*2];
//int indexs[10010*3];//最多3*n个位置(用index做名字会出错)
bool map[10010];
Poster posters[10010];
scanf("%d",&c);
while(c>0){
--c;
scanf("%d",&n);
//printf("n=%d;",n);
int l,u;
for(int i=0;i<n;i++){
scanf("%d%d",&l,&u);
pos[2*i] = posters[i].l = l;
pos[2*i+1] = posters[i].u = u;
//printf("l=%d;u=%d;\n",l,u);
}
//离散化端点值为排序后的下标
//qsort(pos,2*n,sizeof(int),CMP);
//sort(pos,pos+2*n);
Quicksort(pos,2*n);
//整理端点值,把相等的保留一个,把不相邻的两个端点之间插一个任意数
int cnt = 0;//在indexs数组中记录从indexs[1]开始,cnt标识前一个有效位置
indexs[++cnt]=pos[0];
for(int i = 1;i<2*n;i++){
if(pos[i] != indexs[cnt]){
if(pos[i] > indexs[cnt]+1){//大于1,需要插入一个节点
indexs[++cnt] = pos[i]-1;
}
indexs[++cnt] = pos[i];
}
}
//重新调整poster的端点坐标到[1,cnt]的区间
for(int i=0;i<n;i++){
posters[i].l = BinarySearch(indexs,cnt,posters[i].l);
posters[i].u = BinarySearch(indexs,cnt,posters[i].u);
}
//建树
InTree* root = Build(1,cnt);
//更新数据
for(int i=0;i<n;i++){
Update(root,posters[i].l,posters[i].u,i);
}
//查询
memset(map,false,10010*sizeof(bool));
Query(root,map);
//统计结果
cnt=0;
for(int i=0;i<n;i++){
if(map[i])cnt++;
}
printf("%d\n",cnt);
//删树
Delete(root);
}
return 0;
}