P4198 楼房重建

P4198 楼房重建

题意

坐标轴 \(1 \sim n\)\(n\) 个房子,初始高度都是 \(0\)。你在 \((0,0)\)。有 \(m\) 次修改,每次修改一个房子的高度。问修改后你的视角可以看到多少楼房。即你与楼房顶的连线中间不与其他楼房相交。

\(n,m \le 10^5\)

思路

很熟悉题目名字,但是不记得题目内容,也不记得做法。

维护每个点与 \((0,0)\) 连线的斜率。看到的楼房斜率严格递增,且优先左边的楼房。

直接暴力做,修改是 \(O(1)\),查询是 \(O(n)\)

使用线段树,但是怎么维护答案。

考虑这里的信息是否满足可合并性。维护每个节点满足要求的斜率序列长度。左儿子可以直接合并。右儿子不行。

如果左儿子最大斜率 \(x\) 比右儿子 \(v\) 第一个斜率小,那么可以直接合并右儿子。

否则递归下去,如果 \(ls_v\) 的最大值不超过 \(x\),那么 \(ls_v\) 包是没有贡献的,递归 \(rs_v\)

否则递归 \(ls_v\),此时无需递归 \(rs_v\),因为 \(x\)\(rs_v\) 的贡献已经没有影响了,取而代之的是 \(ls_v\) 的最大值。而这种情况我们在合并 \(v\) 的时候处理过了,所以 \(rs_v\) 的贡献就是 \(tr_v -tr_{ls_v}\)

因为是单点修改,所以没有 tag。被影响的结点个数是 \(O(\log)\) 的,每次合并是 \(O(\log)\) 的,所以修改是 \(O(\log^2)\)

查询是全局查诶,所以查询 \(O(1)\)

诶,这么样子的话为什么不可以再平衡一下复杂度呢?

常数不大。

没有必要的平衡时间复杂度的尝试

增加一下查询的复杂度,怎么增加,分个块?

块长为 \(T\)。每个块建一棵线段树。修改 \(\log^2 T\)。查询 \(\frac{n}{T} \log T\)

\[\log^2 T = \frac{n}{T} \log T \]

理论复杂度大概会减少不大的常数倍吧……

实际上估计又难写常数还大,估计是负优化。

哦,所以这个题一直是 \(\log^2\) 是有原因的。

是不是本来是 \(\log\) 复杂度的都不好平衡?

code

搬上很久之前的代码。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=1e5+3;
int n,m;
ld x,y;
struct tree{
	ld ft,mx;
	int nr;
}tr[N<<4];
int f(ld x,int u){
	if(tr[u].ft>x) return tr[u].nr;
	if(tr[u].mx<=x) return 0;
	if(x>=tr[u<<1].mx) return f(x,u<<1|1);
	else return f(x,u<<1)+tr[u].nr-tr[u<<1].nr;
}
void pushup(int u){
	tr[u].ft=tr[u<<1].ft;
	tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx);
	tr[u].nr=tr[u<<1].nr+f(tr[u<<1].mx,u<<1|1);
}
void change(int u,int l,int r,int x,ld val){
	if(l==r&&l==x){
		tr[u].ft=tr[u].mx=val;tr[u].nr=1;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) change(u<<1,l,mid,x,val);
	else change(u<<1|1,mid+1,r,x,val);
	pushup(u);
}
int main(){
	sf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		sf("%Lf%Lf",&x,&y);
		ld k=y/x;
		change(1,1,n,x,k);
		pf("%d\n",tr[1].nr);
	}
}
posted @ 2025-02-16 20:07  wing_heart  阅读(7)  评论(0)    收藏  举报