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;
}
要拿牌啊

浙公网安备 33010602011771号