链接:http://acm.hdu.edu.cn/showproblem.php?pid=2795
题意:
有一个公告栏,h*m的,给定n个操作,对于每一个操作给定一个数wi,表示有一个通知的大小是1*wi的,每次对于这种操作将这个通知放在尽可能上面尽可能左边的位置中去。
思路:
有h行,将每一行初始为m,表示还有m个空位可以放,那么运用一棵线段树来记录下,行之间的区间最大值,就是可以放下的最大值。
对于每一个操作来说,尽可能的要放在上面,那么也就是这个下标要尽可能的小,所以先判断左儿子是否满足区间最大值 >= 所要求的宽度wi,如果满足的话递归的往左儿子找下去,直到找到叶子节点,更新该节点的值,减小wi;不满足再找右儿子。 先找左儿子再找右儿子保证了尽可能放在上面。
注意:
第一发RE。 然后发现此处的h是
109
,那么按道理来说不能开下这么大的线段树,但是会发现操作的次数n是比较小的,然后想一下不管怎么样最多只需要n行的公告板就够了。因为最坏情况每个操作都是给出m长度的,填充到最后一个也才是n行。所以就只需要n就够了。
感觉要多关注,这些之间的关系,有的时候题目中是有隐含的条件的orzz 还是太年轻了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 200090
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int maxx[M<<2];
int h,m,n;
void pushup(int rt)
{
maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l == r)
{
maxx[rt] = m; //将叶子节点更新为宽度m
return ;
}
int m = (l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
int query(int a,int l,int r,int rt)
{
if(l == r)
{
maxx[rt] -= a; //更新叶子节点
return l; //返回序列编号
}
int m = (l+r)>>1;
int ret;
if(maxx[rt<<1] >= a) ret = query(a,lson); //左儿子的区间最大值 >= a
else ret = query(a,rson);
pushup(rt);
return ret;
}
int main()
{
while(scanf("%d%d%d",&h,&m,&n) == 3)
{
h = min(h,n);
build(1,h,1);
for(int i = 0;i < n;i++)
{
int a;
scanf("%d",&a);
if(maxx[1] < a) printf("-1\n"); //如果整个区间的最大值都小,说明不能放
else printf("%d\n",query(a,1,h,1));
}
}
return 0;
}