[bzoj2957] 楼房重建

题意:

数轴上有$n$个楼房,初始时高度都是0。有$m$次操作,每次将楼房$x$的高度变为$y$。

有一个人站在$(0,0)$,每次操作之后请你求出这个人能看见多少个楼房。

一个楼房能被看见当且仅当$(x,y)$到$(0,0)$的连线不会被任何楼房阻挡。

$n,m\leq 10^{5}$。

 

题解:线段树维护单调栈模板。

首先假设每个位置的权值是斜率,那么实际上就是求单调栈元素个数。

于是这就是一道线段树维护单调栈的模板题。

之前写过但不太清楚,现在再回顾一遍实现方法:

每个线段树节点维护两个值,$trmax$表示该区间权值最大值,$trsiz$表示该区间形成的单调栈的元素个数。

考虑单点修改$p$如何处理:

  • 若$p$在右儿子中,那么不会影响左儿子,递归修改右儿子即可。(不会影响儿子指不会影响儿子在父亲单调栈中的贡献)
  • 若$p$在左儿子中,那么在修改左儿子后可能会影响右儿子,此时我们需要设计一个$calc$操作来$pushup$。

$calc(l,r,val,k)$的含义是:当单调栈的初始值为$val$时,$[l,r]$这个区间形成的单调栈的元素个数。

那么$pushup$就可以写了:$trsiz(k)=trsiz(lson)+calc(mid+1,r,trmax(lson),rson)$。

考虑$calc$的值如何计算:

  • 若$val>=trmax(lson)$,那么左儿子不会产生贡献,递归右儿子即可。答案为$calc(l,mid,val,lson)$。
  • 若$val<trmax(lson)$,那么两个儿子都会产生贡献,答案为$calc(l,mid,val,lson)+calc(mid+1,r,val,rson)$。

但这样做的话每$pushup$一次可能要遍历整棵线段树,复杂度就是$O(n)$的,无法接受。

容易发现$calc(mid+1,r,val,rson)$这个值在$val<trmax(lson)$时实际上就是$k$的单调栈中右半部分的元素个数。

那么这个个数就等于$trsiz(k)-trsiz(lson)$。注意不是$trsiz(rson)$,这是没考虑左儿子时右儿子的贡献。

这样每次$pushup$的复杂度是$O(logn)$,总复杂度为$O(nlog^{2}n)$。

实际上所有线段树维护单调栈的题都是大同小异,区别可能只在于如何快速计算某点单调栈中右半部分的贡献。

 

代码:

#include<bits/stdc++.h>
#define maxn 100005
#define maxm 500005
#define inf 0x7fffffff
#define eps 1e-8
#define ll long long
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
double trmx[maxn<<2];
int trsz[maxn<<2];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline int qry(int l,int r,double v,int k){
    //cout<<l<<" "<<r<<":"<<trmx[k]<<" "<<v<<endl;
    if(l==r) return trmx[k]>v;
    int mid=l+r>>1;
    if(v>trmx[k<<1]+eps) return qry(mid+1,r,v,k<<1|1);
    else return qry(l,mid,v,k<<1)+trsz[k]-trsz[k<<1];    
}
inline void upd(int l,int r,int x,double y,int k){
    if(l==r){trmx[k]=y,trsz[k]=1;return;} 
    int mid=l+r>>1;
    if(x<=mid) upd(l,mid,x,y,k<<1);
    else upd(mid+1,r,x,y,k<<1|1); 
    trmx[k]=max(trmx[k<<1],trmx[k<<1|1]);
    trsz[k]=trsz[k<<1]+qry(mid+1,r,trmx[k<<1],k<<1|1);
    //cout<<l<<" "<<r<<"::::::"<<trmx[k]<<" "<<trsz[k]<<endl;
}

int main(){
    int n=read(),Q=read();
    while(Q--){
        int x=read(),y=read();
        upd(1,n,x,(double)((y+0.0)/(x+0.0)),1);
        printf("%d\n",trsz[1]);
        //fgx;
    } 
    return 0;
}
楼房重建
posted @ 2019-12-12 10:53  Fugtemypt  阅读(227)  评论(0编辑  收藏  举报