P4198 楼房重建

题意:

\(n\)栋待建的楼房,站在\((0,0)\)点,对于楼房\(i\)来说,如果从原点能看到楼房的楼顶且没有楼房阻挡,就算能看到该楼房(?,每次对于一座楼房可以增加高度和减小高度,每次修改后问最多能看到多少栋楼房?

题解:

线段树维护区间斜率最大值,以及区间最长上升子序列(即斜率递增)的长度,难点在于区间的合并:
对于每次修改:可以log递归去合并该区间,那么每次修改的时间复杂度为\(({logn}^2)\)
对于区间合并:
很显然的一点,就是一个区间的第一个元素是一定可以看到的,因为前面没有任何的房屋阻挡。
1.如果左区间的斜率最大值小于右区间的第一个值,那么总区间的最长上升子序列的长度为左右最长上升子序列的和。
2.如果左区间的最大值大于右区间最大值,那么右区间的所有都不能看到。
3.否则递归处理该区间的左右子区间,如果左子区间最大值小于刚才的最大值,那么递归处理右区间,否则处理左区间加上右区间的答案。

#include "bits/stdc++.h"
using namespace std;
const int N=1e5+5;
int n,m;
double a[N];
struct node{
	double mx;
	int len;
	#define l(x) b[x].len
	#define m(x) b[x].mx
}b[4*N];
void push(int p){
	m(p)=max(m(p<<1),m(p<<1|1));
}
int pushup(double tmp,int s,int t,int p){
	if(m(p)<=tmp)	return 0;
	if(a[s]>tmp)	return l(p);
	if(s==t)		return a[s]>tmp;
	int ls=p<<1,rs=p<<1|1;
	int mid=(s+t)/2;
	if(m(ls)<=tmp)	return pushup(tmp,mid+1,t,p<<1|1);
	else			return pushup(tmp,s,mid,p<<1)+l(p)-l(rs);
}
void update(int s,int t,int p,int c,int w){
	if(s==t&&s==c){
		m(p)=(double)w/c;
		l(p)=1;
		return ;
	}
	int mid=(s+t)/2;
	if(c<=mid)	update(s,mid,p<<1,c,w);
	else		update(mid+1,t,p<<1|1,c,w);
	push(p);
	l(p)=l(p<<1)+pushup(m(p<<1),mid+1,t,p<<1|1);
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;scanf("%d %d",&x,&y);
		a[x]=(double)y/x;
		update(1,n,1,x,y);
		printf("%d\n",b[1].len);
	}
	
	return 0;
}
posted @ 2021-03-02 20:24  喜欢李知恩IU  阅读(55)  评论(0)    收藏  举报