题解:楼房重建

题目传送门

题意分析

设第 \(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 代码

//#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;
}
posted @ 2026-02-05 11:05  TH911  阅读(4)  评论(0)    收藏  举报