NOJ1074南二的公告牌——线段树+单点更新

44 篇文章 1 订阅

南二的公告牌

Time Limit(Common/Java):2500MS/7500MS          Memory Limit:65536KByte
Total Submit:360            Accepted:19

Description

南二有一个巨大的长方形公告牌,尺寸为h×w(h是它的高度,w是它的宽度)。牌子上可以张贴一切可能的告示:最近ACM程序设计竞赛通知,食堂菜单的变化,以及其他重要信息。

在9月1日,公告牌是空的。告示一个接一个地张贴在广告牌上。

每个告示是单位高度的纸条。具体地说,第i个告示是一个大小1 ×wi的矩形。当有人在公告牌上贴出一个新的告示,他永远选择尽可能上的位置。在所有可能的最高位置,她将永远选择最左边的一个。

如果没有有效的位置来贴一个新的告示,则不放在公告牌上。给定公告牌的大小,你的任务是找到告示放置的行数。

Input

输入文件的第一行包括三个整数:hw, 和 (1 ≤ h,w ≤ 109; 1 ≤ n ≤ 200 000) —表示公告牌的大小和告示的数目。

下面n行,每行包括一个整数wi (1 ≤wi ≤ 109) 表示 第i个告示的宽度。

Output

对于每个告示(按照它们在输入文件中给定的顺序)输出一个值 — 这个告示放置的行数。行从1到h标识,从顶行开始。如果一个告示不能放置于告示牌,则输出“-1”。

 

注意:输出部分的结尾要求包含一个多余的空行。

Sample Input

3 5 5
2
4
3
3
3

Sample Output

1
2
1
3
-1

Source

“IBM南邮杯”个人赛2009


分析:线段树+单点更新。很重要,要掌握。

一开始自己写的提交Test15一直超时。想想应该是我的update写的不太好。

然后应用了query+update比较标准的写法就AC了。

一开始的代码(错误的):其实我想多了。确实会超时,都没用到mid

#include<iostream>
using namespace std;
#define MIN(a, b) (a<b?a:b)
#define MAX(a, b) (a>b?a:b)

//南二的公告牌

__int64 h, w;
int n;
bool flag; // 只需一次

struct Node
{
	int l;
	int r;
	__int64 value; // 该子树下最大空余
}tree[800004];

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].value = w;
	if(l == r) return;
	int mid = (l+r)>>1;
	build(v<<1, l, mid);
	build(v<<1|1, mid+1, r);
}

void update(int v, int wi, int curr)
{
	if(tree[1].value < wi)
	{
		printf("-1\n");
		return;
	}
	if(tree[v].value >= wi && tree[v].l == tree[v].r)
	{
		if(flag == true)
		{
			tree[v].value -= wi;
			printf("%d\n",tree[v].l); // l == r
			flag = false;
			return;
		}
	}

	if(tree[v<<1].value >= wi) update(v<<1, wi, curr<<1);
	if(tree[v<<1|1].value >= wi) update(v<<1|1, wi, curr<<1|1);
	if(tree[curr].l != tree[curr].r)
		tree[curr].value = MAX(tree[curr<<1].value, tree[curr<<1|1].value);
}

int main()
{
	__int64 wi;
	scanf("%64I%64I%d",&h,&w,&n);
	build(1, 1, MIN(h, n)); 
	while(n --)
	{
		scanf("%d",&wi);
		flag = true;
		update(1, wi, 1);
	}

	return 0;
}


正确的做法:
update标准的写法是:update(int v, int l, int r, int m) // 更新区间l~r 加上数m
因为这里l == r所以就只写了l。curr用来记录下标更新value值(该子树下最大空余)

void update(int v, int l, int curr) // l == r
{
	if(tree[v].l == l && tree[v].r == l)
	{
		tree[v].value -= wi;
		return;
	}
	int mid = (tree[v].l+tree[v].r)>>1;
	if(l <= mid)
		update(v<<1, l, curr<<1);
	else if(l > mid)
		update(v<<1|1, l, curr<<1|1);
	tree[curr].value = MAX(tree[curr<<1].value, tree[curr<<1|1].value);
}

int query(int v, int l, int r)
{
	if(tree[v].value < wi) return -1;
	if(tree[v].l == tree[v].r) return tree[v].l;
	int mid = (tree[v].l+tree[v].r)>>1;
	if(tree[v<<1].value >= wi) 
		return query(v<<1, l, mid);
	else
		return query(v<<1|1, mid+1, r);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值