问题描述
在大学的入口处,有一个巨大的矩形广告牌,大小为h * w(h是它的高度,w是它的宽度)。董事会是发布所有可能公告的地方:最近的节目比赛,餐厅菜单的变化以及其他重要信息。
9月1日,广告牌是空的。一个接一个,公告开始被放在广告牌上。
每个公告都是单位高度的纸条。更具体地,第i个通告是大小为1 * wi的矩形。
当有人在广告牌上发布新的公告时,她总是会选择公告的最高位置。在所有可能的最高职位中,她总是选择最左边的职位。
如果新公告没有有效位置,则不会将其放在广告牌上(这就是为什么某些编程竞赛没有来自该大学的参与者)。
根据广告牌和公告的大小,您的任务是查找公告所在的行数。
输入
有多种情况(不超过40例)。
输入文件的第一行包含三个整数,h,w和n(1 <= h,w <= 10 ^ 9; 1 <= n <= 200,000) - 广告牌的尺寸和公告数量。
接下来的n行中的每一行包含整数wi(1 <= wi <= 10 ^ 9) - 第i个通告的宽度。
产量
对于每个公告(按输入文件中给出的顺序)输出一个数字 - 放置此公告的行号。从第一行开始,行从1到h编号。如果无法在广告牌上刊登公告,请为此公告输出“-1”。
样本输入
3 5 5
2
4
3
3
3
样本输出
1
2
1
3
-1
分析:
建立一颗线段树,线段树维护的每个元素(不是指树的节点哦)代表广告牌的一行的当前剩余最大空间maxv。比如i节点维护区间[l,r],那么k(l<=k<=r)代表广告牌的第k行。即maxv[i]就是i节点维护的广告牌的那些行中,剩余空间的最大值。
其中线段树中节点维护的信息是:本节点控制的区间[L,R]内的叶节点的最大剩余空间maxv[i]。如果maxv[i]>wi说明这个节点的子树有叶子能放下wi,优先往左子树找即可.
代码:
<span style="font-size:18px;"><span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int H,W,n,Q;
const int MAXN=200000+100;
int maxv[MAXN*4];
int id[MAXN*4];
int cnt;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
void PushUp(int i)
{
maxv[i]=max(maxv[i*2] , maxv[i*2+1]);
}
void build(int i,int l,int r)
{
if(l==r)
{
maxv[i]=W;
id[i]=++cnt;
return ;
}
int m=(l+r)/2;
build(lson);
build(rson);
PushUp(i);
}
int update(int w,int i,int l,int r) //这里的跟新就是找到大于等于w的最小的那个叶节点。
{
if(w>maxv[i])
return -1;
if(l==r)
{
maxv[i]-=w;
return id[i];
}
int res=-1;
int m=(l+r)/2;
if(maxv[i*2]>=w)res = update(w,lson);//这里优先考虑左子树。
else if(maxv[i*2+1]>=w)res= update(w,rson);
PushUp(i);
return res;
}
int main()
{
while(scanf("%d%d%d",&H,&W,&Q)==3)
{
cnt=0;
n = min(Q,H);//线段树叶节点的最大数目
build(1,1,n);
for(int i=1;i<=Q;i++)
{
int w;
scanf("%d",&w);
printf("%d\n",update(w,1,1,n));
}
}
return 0;
}</span></span>