题意:大致就是给定不同长度但宽度相同的海报,在长度一定的墙体上先后粘贴。问最后能够看到的有几个海报。
思路:事实上,对于越靠后粘贴的海报,它的优先级总是最高的。那么我们可以考虑对输入的数据进行倒序处理。
简单来说就是,如果一个海报在粘贴的时候发现它粘贴的范围内已经全部被一个优先级高的海报粘贴好了,
那么我们就无法最后看到这个海报。
那么应该选择什么样的数据结构来维护这个数组呢?
这是一个典型的数组范围的维护的题目,一般我们会选择线段树或者差分数组。而对于这道题目而言,
我们发现,题目本身关注的更多是在海报边界(也就是一张海报从哪里开始一直延伸到哪里),所以这里可以
舍弃差分数组。而且,本题目中,只要优先级高的海报粘贴了,那么之后所有的优先级低的海报都无法粘贴。
所以这里没有必要用差分数组衡量一个位置一共粘贴了多少层海报,而是能够有没有粘贴。相比之下,线段树更加灵活。
但是,这个题目的数组规模是1e7,而且是多测试。所以我们不能单纯的每一次都对一个1e7的数组进行线段树维护,
也没有必要这样进行维护。加之题目关注海报边界。我们提出一个相对巧妙的解决方案:
根据每一次所有海报拥有的边界,将边界进行排序。并通过hash映射出一个新的海报墙。
这样就很好的在绝大多数情况下简化了线段树的维护规模。
下面,就用代码进行实现:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
class Node{//线段树结点
public:
int l,r;
bool cover;
int mid(){return (l+r)>>1;}
Node *lson,*rson;
Node(){};
};
const int maxn = 10000000 + 100;//10000000
Node psd[maxn];//线段树线性数组
class Query{
public:
int l,r;
};
Query aim[10100];//存储各个海报的信息
int num[10100*2];//重排列边界
int hasher[maxn];//哈希映射
int curs = 1;
//建立线段树
void BuildTree(Node* root,int l,int r){//[l,r)
root->l = l;
root->r = r;
root->cover = false;
if(l==r){//树的叶子节点的处理
root->lson = root->rson = NULL;
return;
}
//一定要注意下面这个指针的赋值!!!!
root->lson = psd + curs;
curs++;
root->rson = psd + curs;
curs++;
int mid = (l+r) >> 1;
BuildTree(root->lson,l,mid);
BuildTree(root->rson,mid+1,r);
}
//判断一张海报能不能在粘贴后显露出来-只要在[l,r)范围内有一个位置没有被遮挡即可
bool isPostandVis(Node* root,int l,int r){//[l,r)
if(root->cover)return false;
if(root->l == l && root->r == r)return root->cover = true;
//下面的代码用于拆分区间
bool ans;
if(r <= root->mid()){
ans = isPostandVis(root->lson,l,r);
}else if(l > root->mid()){
ans = isPostandVis(root->rson,l,r);
}else{
bool a = isPostandVis(root->lson,l,root->mid());
bool b = isPostandVis(root->rson,root->mid()+1,r);
ans = a||b;
}
//优化更新线段树某结点子树的状态
if(root->lson->cover && root->rson->cover){
root->cover = true;
}
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
int len = 0;
curs = 1;
for(int i=0;i<n;i++){
scanf("%d %d",&aim[i].l,&aim[i].r);
//将所有的边界信息全部放到num数组里
num[len++] = aim[i].l;
num[len++] = aim[i].r;
}
sort(num,num+len);//排序边界-忽略所有海报的非边界部分
len = unique(num,num+len) - num;//排除重复
for(int i=0;i<len;i++){
hasher[num[i]] = i+1;//映射成[1,len+1)
}
//下面开始建立线段树
BuildTree(psd,1,len+1);
//下面开始贴海报
int cnt = 0;
for(int i=n-1;i>=0;i--){//从后向前遍历-优先级由高到低
if(isPostandVis(psd,hasher[aim[i].l],hasher[aim[i].r])) cnt++;
}
cout<<cnt<<endl;
}
return 0;
}
感谢博客-https://my.oschina.net/u/1017188/blog/333905
OK