标准解法是用的离散化。此题的特殊性在于普通离散化后,两个相邻的值之间的区间也可能是有效的区间。例如:
有三张海报[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; }