【洛谷4198】楼房重建【线段树】【单调】

传送门~

单点修改,维护从左看到的斜率升序序列长度。

每个楼房的高度除以距离得到斜率。

所以看出这道题需要动态维护一个单调上升序列。

考虑区间合并。记录区间答案长度以及区间最大值。

很明显左区间仍然可以被看到。而右区间不一定。

右区间能别看到的,只有右区间合法答案中,比左区间最大值还大的部分。

考虑在右区间上二分。因为右区间维护的也是一个单调栈,所以可以二分。

如果到叶子节点,判断是否大于左区间最大值。

如果当前左区间最大值大于要求最大值,则返回在左区间中查找的答案加上,本区间长度减去左区间长度。

注意,此时不应该直接提取右边答案。因为我们需要的,是在本区间单调栈中,属于右边的部分,而非仅考虑右边的答案。这是有区别的。

剩下的大家都很熟悉,二分就是了。

在每次单点修改后,区间长度就是左区间长度加上,在右区间中,大于左区间最大值的长度。

#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;ch=getchar();
	}return cnt*f;
}
int n,m;
struct node{
	int len,l,r;
	double maxx;
}t[400003];
void build(int u,int l,int r){
	t[u].l=l;t[u].r=r;if(l==r)return;
	int mid=(l+r)>>1;build(u*2,l,mid);build(u*2+1,mid+1,r);
}
double a[100003];
void pushup(int u){
	t[u].maxx=max(t[u*2].maxx,t[u*2+1].maxx);
}
int pushup2(int u,double key){
	if(t[u].l==t[u].r)return t[u].maxx>key;
	if(t[u].maxx<=key)return 0;
	if(a[t[u].l]>key)return t[u].len;
	if(t[u*2].maxx>key)return t[u].len-t[u*2].len+pushup2(u*2,key);
	else return pushup2(u*2+1,key);
}
void modify(int u,int pos,double key){
	if(t[u].l>pos||t[u].r<pos)return;
	if(t[u].l==t[u].r&&t[u].r==pos){
		t[u].maxx=key;t[u].len=1;return;
	}
	modify(u*2,pos,key);modify(u*2+1,pos,key);
	pushup(u);
	t[u].len=t[u*2].len+pushup2(u*2+1,t[u*2].maxx);
}
signed main(){
	n=in;m=in;
	build(1,1,n);
	while(m--){
		int x=in;int y=in;a[x]=(double)y/x;
		modify(1,x,a[x]);
		cout<<t[1].len<<'\n';
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值