题意:给一个h*w的公告牌,h是高度,w是宽度,一个单位高度1为一行,然后会有一些公告贴上去,公告是1*wi大小的长纸条,优先贴在最上面并且最左边的位置,如果没有空间贴得下,就输出-1,可以的话,就输出所贴的位置(第几行)。
思路:建线段树时取的n,我们只需要行的数量,这个10^9很是吓人,只需要取 min(h,n)即可,因为h太多也是无用的,总共才n个广告吗,多的行建出来只是浪费空间。然后结点的val值表示node[u].l到node[u[.r这个区间能容纳的最大宽度,叶子结点就是代表其所对应的行的剩余宽度。每次query时,先看左边的能否满足,然后看右边的。
如果理清了思路就很简单了,但是本人不知道为啥,我用自己宏定义的max 和min就超时,用系统的就直接AC。
#include<cstdio>
#include<cstring>
#include<iostream>
#define L(u) (u<<1)
#define R(u) (u<<1|1)
using namespace std;
const int N = 200001;
int h,w,n,tot;
struct Node {
int l,r,val;
};
Node node[N*3];
void pushUp(int u)
{
node[u].val = max(node[L(u)].val , node[R(u)].val);
}
void build(int u,int left,int right)
{
node[u].l = left,node[u].r = right;
node[u].val = w;
if(node[u].l==node[u].r)
{
return;
}
int mid = (node[u].l+node[u].r)>>1;
build(L(u),left,mid);
build(R(u),mid+1,right);
}
void upDate(int u,int r,int x)//3个参数分别表示现在更新的结点,r表示需要更新的行,x表示需要更新的数值
{
if(node[u].l==node[u].r&&node[u].l==r)
{
node[u].val -= x;
return;
}
int mid = (node[u].l+node[u].r)>>1;
if(r<=mid)
{
upDate(L(u),r,x);
}
else
{
upDate(R(u),r,x);
}
pushUp(u);
}
int query(int u,int x)
{
if(node[u].val<x)
return -1;
if(node[u].l==node[u].r&&node[u].val>=x)
return node[u].l;
else
{
if(node[L(u)].val<x)
{
if(node[R(u)].val<x)
{
return -1;
}
else
{
return query(R(u),x);
}
}
else
{
return query(L(u),x);
}
}
}
int main(void)
{
while(scanf("%d %d %d",&h,&w,&n)!=EOF)
{
tot = min(h,n);
build(1,1,tot);
for(int i=1;i<=n;++i)
{
int a;
scanf("%d",&a);
int x = query(1,a);
if(x==-1)
printf("%d\n",x);
else
{
printf("%d\n",x);
upDate(1,x,a);
}
}
}
return 0;
}