题解:楼房重建

洛谷P4198

Posted by TH911 on February 5, 2026

题目传送门

题意分析

设第 $i$ 栋楼房的高度为 $h_i$。

那么 $(i,h_i)$ 能被看见当且仅当:

\[\max_{j=1}^{i-1}\dfrac{h_j}j<\dfrac{h_i}{i}\]

记 $k_i=\dfrac{h_i}i$,则题意即动态维护一个序列,$k_1$ 必须选,且选中的 $k_i$ 均为前缀严格最大值

普通静态方法都不行,不能接受。考虑充分利用已有信息,那么容易想到线段树合并区间。

对于节点 $p$,维护 $\textit{len}_p$ 表示 $[l_p,r_p]$ 的最长长度。

考虑 $[l,\textit{mid}],[\textit{mid}+1,r]$ 合并到 $[l,r]$。

显然 $[l,\textit{mid}]$ 部分的答案一定不变,重要的是如何求出 $[\textit{mid}+1,r]$ 的答案。右子区间 $[\textit{mid}+1,r]$ 内最终选中的所有 $k_i$ 必须都要大于左子区间 $[l,\textit{mid}]$ 的最大值,记为 $s=\max[l,\textit{mid}]$。

对于 $[\textit{mid}+1,r]$ 内,设其左子区间 $[l’,\textit{mid}’]$、右子区间 $[\textit{mid}’+1,r’]$。

  • 若 $\max[l’,\textit{mid}’]\leq s$,则 $[l’,\textit{mid}’]$ 一定不在答案内,答案在 $[\textit{mid}’+1,r]$ 中。

  • 若 $\max[l’,\textit{mid}’]>s$,则 $[l’,\textit{mid}’]$ 一定在答案内,需要先计算这一部分。

    考虑 $[\textit{mid}’+1,r’]$ 的答案是什么,和上面是类似的,这一部分要大于 $\max[l’,\textit{mid}’+1]$。

    因此直接得到这一部分的答案为 $\textit{len}{[l’,r’]}-\textit{len}{[l’,\textit{mid}’]}$。

    由于线段树的性质,处理 $[l,r]$ 时,这些信息都已知。

这么看下来,这就是一个递归求解的过程,单次复杂度 $\mathcal O(\log n)$。

形式化地,设 $\operatorname{find}(p,s)$ 表示 $[l_p,r_p]$ 内大于 $s$ 的答案(不一定从 $l_p$ 开始),$\textit{lChild}_p,\textit{rChild}_p$ 分别为左右子节点,$\textit{max}_p$ 表示 $[l_p,r_p]$ 内的最大值。

则:

\[\operatorname{find}(p,s)= \begin{cases} 0&\textit{max}_p\leq s\\ 1&l_p=r_p\\ \operatorname{find}(\textit{rChild}_p,s)&\textit{max}_{\textit{lChild}_p}\leq s\\ \operatorname{find}(\textit{lChild}_p,s)+\textit{len}_p-\textit{len}_{\textit{lChild}_p}&\text{otherwise.} \end{cases}\]

总时间复杂度 $\mathcal O\left(m\log^2n\right)$。

AC 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=1e5;
struct k{
	int x,y;
	
	k(){
		x=1,y=-2147483647;
	}
	k(int x2,int y2){
		x=x2,y=y2;
	}
};
bool operator <(k a,k b){
	return 1ll*a.y*b.x < 1ll*b.y*a.x;
}
bool operator >(k a,k b){
	return 1ll*a.y*b.x > 1ll*b.y*a.x;
}
bool operator ==(k a,k b){
	return 1ll*a.y*b.x == 1ll*b.y*a.x;
}
bool operator <=(k a,k b){
	return !(a>b);
}
struct segTree{
	struct node{
		int l,r,len;
		k max;
	}t[N<<2|1];
	
	
	int find(int p,k s){
		if(t[p].max<=s){
			return 0;
		}else if(t[p].l==t[p].r){
			return 1;
		}else if(t[p<<1].max<=s){
			return find(p<<1|1,s);
		}else{
			return find(p<<1,s)+t[p].len-t[p<<1].len;
		}
	}
	void up(int p){
		t[p].max=max(t[p<<1].max,t[p<<1|1].max);
		t[p].len=t[p<<1].len+find(p<<1|1,t[p<<1].max);
	}
	void build(int p,int l,int r){
		t[p]={l,r};
		if(l==r){
			return;
		}
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		up(p);
	}
	void set(int p,int x,int y){
		if(t[p].l==t[p].r){
			t[p].max={x,y};
			t[p].len=1;
			return;
		}
		if(x<=t[p<<1].r){
			set(p<<1,x,y);
		}
		if(t[p<<1|1].l<=x){
			set(p<<1|1,x,y);
		}
		up(p);
	}
	int query(){
		return t[1].len;
	}
}t;
main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int n,m;
	cin>>n>>m;
	t.build(1,1,n);
	while(m--){
		int x,y;
		cin>>x>>y;
		t.set(1,x,y);
		cout<<t.query()<<'\n';
	}
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}