题意描述:
在一条直线上有n棵树,每棵树有一个高度。现在进行查询:给一个高度h,把小于等于h的树砍掉,剩余的树能组成几个块?
块的定义:一个块要包含尽可能多的位置连续的树,而且被包含的树没有被砍掉
解题思路:离线处理
1、逆向思维:砍树的对立面就是长树
(1)如果我们把树的高度和查询高度都按从大到小的顺序排序,初始化直线上没有一棵树存在;
(2)然后从前往后扫描查询,对于当前查询,我们只需要把高于当前查询的树生长出来(vis数组标记即可)
(3)在每颗树生长的同时检测其两边的树是否已经生长,对于不同的情况分类讨论即可
源代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 50010
using namespace std;
struct node{
int h;
int index;
};
bool cmp(node a,node b){
return a.h>b.h;
}
node d[MAXN];///树的高度
node q[MAXN];///查询高度
bool vis[MAXN];///标记树是否已经生长
int ans[MAXN];///每个查询的连通块
int n,m;
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=0;i<n;++i)
{
scanf("%d",&d[i].h);
d[i].index=i;
}
sort(d,d+n,cmp);
for(int i=0;i<m;++i){
scanf("%d",&q[i].h);
q[i].index=i;
}
sort(q,q+m,cmp);
int id=0,iq=0;
int tans=0;
memset(vis,false,sizeof(vis));
while(iq<m){
if(id<n){
if(q[iq].h>=d[id].h){///没有可以生长的树
ans[q[iq].index]=tans;
iq++;
}
else{
for(;id<n&&d[id].h>q[iq].h;++id){
int tl=d[id].index-1;
int tr=d[id].index+1;
if(tl>=0&&tr<n&&vis[tl]&&vis[tr])///如果树两边的树都已经生长,加上这棵树之后构成一个块,由2变为1
tans--;
///两边的树如果都还没有生长,则块+1
if(tl>=0){
if(!vis[tl]){
if(tr<n){
if(!vis[tr])
tans++;
}
else
tans++;
}
}
else{
if(tr<n){
if(!vis[tr])
tans++;
}
else
tans++;
}
vis[tl+1]=true;
}
ans[q[iq].index]=tans;
}
}
else///所有的树已经生长完
{
ans[q[iq].index]=tans;
iq++;
}
}
for(int i=0;i<m;++i)
printf("%d\n",ans[i]);
}
return 0;
}
2、正向思维:
1、我们把树的高度和查询高度从小到大排序,从前往后处理每个查询,初始化直线上的所有树都在
2、对于当前查询,我们只需要把小于等于查询高度的树砍掉即可
3、对于每次砍树,检测其两边的树是否被砍掉,分类讨论即可