zoukankan      html  css  js  c++  java
  • [bzoj2957] 楼房重建

    题意:

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

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

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

    $n,mleq 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
    ",trsz[1]);
            //fgx;
        } 
        return 0;
    }
    楼房重建
  • 相关阅读:
    自定义字体(特殊字体)在网页中的应用
    面向对象中的接口和多态
    抽象类和抽象方法
    python简单的函数定义和用法实例
    python简单的函数定义和用法实例
    Python变量和字符串详解
    Python变量和字符串详解
    大数据产业发展 三大模式可毕其功于一役
    大数据产业发展 三大模式可毕其功于一役
    python实现基于两张图片生成圆角图标效果的方法
  • 原文地址:https://www.cnblogs.com/YSFAC/p/12027884.html
Copyright © 2011-2022 走看看