【BZOJ2957】楼房重建

10人阅读 评论(0) 收藏 举报
分类:

【题目链接】

【思路要点】

  • 令\(B_i=\frac{A_i}{i}\),问题等价于从0开始不断地找右侧第一个\(B_i\)严格大于当前位置的位置,一共可以进行多少轮。
  • 考虑用线段树解决本题。
  • 为每个节点\(root\)(对应区间\([L,R]\))记录区间最大值\(Max_{root}\)以及从左侧从区间最大值开始,一共可以在\([L,R]\)范围内进行上述过程的轮数\(val_{root}\)。
  • 假设我们已经维护好了这样的一系列信息,我们现在要支持一种询问\(query(L,R,pre)\)表示从区间左侧某一个\(B_i\)等于\(pre\)的位置开始,一共可以在\([L,R]\)范围内进行上述过程的轮数。
  • 若\(pre≥Max_{leftchild}\),那么\([L,Mid]\)内不会有任何一个数被找到,因此返回\(query(Mid+1,R,pre)\)。
  • 否则,\(val_{root}\)就是右侧会被找到的数的个数,返回\(query(L,Mid,pre)+val_{root}\)。
  • 可以发现,单次调用\(query\)的复杂度是\(O(LogN)\)的。
  • 因此,我们可以借助\(query\)在修改后维护线段树上的信息。
  • 时间复杂度\(O(N+MLog^2N)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct frac {int x, y; };
bool operator == (frac a, frac b) {return 1ll * a.x * b.y == 1ll * a.y * b.x; }
bool operator < (frac a, frac b) {return 1ll * a.x * b.y < 1ll * a.y * b.x; }
bool operator > (frac a, frac b) {return 1ll * a.x * b.y > 1ll * a.y * b.x; }
bool operator <= (frac a, frac b) {return 1ll * a.x * b.y <= 1ll * a.y * b.x; }
bool operator >= (frac a, frac b) {return 1ll * a.x * b.y >= 1ll * a.y * b.x; }
struct SegmentTree {
	struct Node {
		int lc, rc, val;
		frac Max;
	} a[MAXN * 2];
	int size, root, n;
	void build(int &root, int l, int r) {
		root = ++size;
		a[root].Max = (frac) {0, 1};
		a[root].val = 0;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	int query(int root, int l, int r, frac pre) {
		if (l == r) return a[root].Max > pre;
		int mid = (l + r) / 2;
		if (a[a[root].lc].Max > pre) return query(a[root].lc, l, mid, pre) + a[root].val;
		else return query(a[root].rc, mid + 1, r, pre);
	}
	void update(int root, int l, int r, int mid) {
		a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max);
		a[root].val = query(a[root].rc, mid + 1, r, a[a[root].lc].Max);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void modify(int root, int l, int r, int pos, frac val) {
		if (l == r) {
			a[root].Max = val;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, val);
		else modify(a[root].rc, mid + 1, r, pos, val);
		update(root, l, r, mid);
	}
	void modify(int x, frac val) {
		modify(root, 1, n, x, val);
	}
	int query() {
		return query(root, 1, n, (frac) {0, 1});
	}
} ST;
int n, m;
int main() {
	read(n), read(m);
	ST.init(n);
	while (m--) {
		int x, y;
		read(x), read(y);
		ST.modify(x, (frac) {y, x});
		writeln(ST.query());
	}
	return 0;
}


查看评论

BZOJ2957 楼房重建(线段树)

【题解】 题目描述有误,一栋楼房可见,应满足其最高点到(0,0)的连线不与其他楼房相交  这个条件可以等价为:它的斜率比之前的任何一个都大(相等也不行) 因此,只需O(logN)修改、O(log...
  • cjk_cjk
  • cjk_cjk
  • 2015年06月21日 15:17
  • 1070

BZOJ 2957 楼房重建 (分块)

很容易观察到其实就是在找斜率的最长上升序列,分块之后直接处理斜率,怕有精度误差所以用的乘法做判断小于关系,加了一点小优化卡过去了 直接用double表示斜率貌似也可过/***************...
  • Forever_wjs
  • Forever_wjs
  • 2016年10月10日 18:36
  • 267

bzoj 2957: 楼房重建(线段树)

2957: 楼房重建 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 1143  Solved: 541 [Submit][Status][Di...
  • clover_hxy
  • clover_hxy
  • 2016年05月10日 11:49
  • 1065

bzoj2957楼房重建(线段树)

Description   小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。   为了简化问...
  • H_Anonymity
  • H_Anonymity
  • 2017年10月07日 09:17
  • 74

线段树 bzoj2957 楼房重建

大概意思就是求序列从一位置开始的动态上升序列。 分块可过,但这一类题目其实可用线段树。 也就是维护每个区间的上升序列长度。下面这种求法只是用于当前节点所覆盖的区间完全被查询区间覆盖。 具体而言,...
  • QTY2001
  • QTY2001
  • 2017年10月07日 11:43
  • 95

[bzoj2957][线段树]楼房重建

Description   小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。   为了简化...
  • Rose_max
  • Rose_max
  • 2017年12月23日 11:41
  • 404

BZOJ2957 楼房重建 【线段树】

题目   小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。   为了简化问题,我们考虑这些事件发...
  • qq_38678604
  • qq_38678604
  • 2018年01月11日 17:45
  • 60

【线段树】BZOJ2957 楼房重建

题面在这里通过观察可以发现:能看到的楼房中,最高点与原点连线的斜率一定是递增的所以就是求最长上升序列的长度用线段树维护即可,与BZOJ2770类似示例程序:...
  • linkfqy
  • linkfqy
  • 2017年09月02日 14:21
  • 505

[BZOJ2957]楼房重建(线段树)

明天就要走啦!今天ATP不用上晚自习啦!
  • FromATP
  • FromATP
  • 2016年11月17日 09:38
  • 217

[bzoj2957]楼房重建 线段树

你热爱生命吗?那么别浪费时间,因为时间是组成生命的材料。
  • youhavepeople
  • youhavepeople
  • 2017年07月04日 17:09
  • 117
    个人资料
    持之以恒
    等级:
    访问量: 9644
    积分: 2663
    排名: 1万+
    文章分类
    文章存档