题目链接:
https://vjudge.net/problem/HDU-2795
题目大意:
有一块高为h、宽为w的展览板(展览板的高度从上至下记录为1-h),要往上面贴告示,要求从上往下贴、相同高度往左贴,每条告示高为1,宽为len,问每次贴告示最高多高(尽量使高度值最小)。
题目分析:
这道题的关键在于线段树表示什么:线段树的底层节点表示对应的高度剩余长度有多少(因为是从左往右贴的,所以不存在剩余长度是由多部分组成的情况,也就是说剩多少长度就能贴多少长度的告示),而每个父节点表示其对应区间内剩余的最大长度。对每个父节点,如果左子节点的剩余最大长度大于告示长度进左节点,否则进右节点(相同高度往左贴的实现)。
输入一个长度,先将其和根节点的剩余最大长度比较,如果比其大即所有的都贴不下,直接输出-1。
此外,开结构体的时候大小开min(布告栏高度,告示数量)即可,因为告示数量占用的最大高度就是n。
#include<iostream>
#include<stdio.h>
using namespace std;
#define maxn 200010 //此题h的范围是10^9,n的范围是200000,因此开200000+10即可
struct TREE{
int l,r;
int s; //对应区间剩余最大长度
}t[maxn*4];
int h,w;
void buildt(int k,int l,int r){
t[k].s=w;
t[k].l=l;
t[k].r=r;
if(l==r) return;
int mid=(t[k].l+t[k].r)/2;
buildt(2*k,l,mid);
buildt(2*k+1,mid+1,r);
}
int update(int k,int l,int r,int x){
if(t[k].l==t[k].r){ //已是底层节点,找到了明确的高度
t[k].s-=x; //对应区间的剩余最大长度会减小
return t[k].r; //询问的答案
}
int sum1=0,sum2=0;
if(x<=t[2*k].s)
sum1=update(2*k,l,r,x);
else sum2=update(2*k+1,l,r,x);
t[k].s=max(t[2*k].s,t[2*k+1].s); //回溯父节点
}
int main(){
int n,h,len;
while(cin>>h>>w>>n){
buildt(1,1,min(n,h));
for(int i=1;i<=n;i++){
scanf("%d",&len);
if(len>t[1].s)
cout<<"-1"<<endl;
else cout<<update(1,1,min(n,h),len)<<endl;
}
}
return 0;
}
而其中update函数中的更新子节点并获得答案这样写是不对的:
if(x<=t[2*k].s)
return update(2*k,l,r,x);
else return update(2*k+1,l,r,x);
t[k].s=max(t[2*k].s,t[2*k+1].s);
这样写回溯父节点的语句执行不成功。