zoukankan      html  css  js  c++  java
  • BZOJ2957: 楼房重建

    BZOJ2957: 楼房重建

    Description

      小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
      为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
      施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大---修建,也可以比原来小---拆除,甚至可以保持不变---建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?

    Input

      第一行两个正整数N,M
      接下来M行,每行两个正整数Xi,Yi

    Output

      M行,第i行一个整数表示第i天过后小A能看到的楼房有多少栋

    Sample Input

    3 4
    2 4
    3 6
    1 1000000000
    1 1

    Sample Output

    1
    1
    1
    2
    数据约定
      对于所有的数据1<=Xi<=N,1<=Yi<=10^9
    N,M<=100000

    题解Here!
    被数学+字符串+图论虐到滚回来水数据结构的我。。。

    显然可以想到进行斜率处理,通过斜率的单调递增来求出$len$。

    其实答案就是整个$1-n$区间中从第一项开始,每一个大于前一项的必选,小于等于前一项的必须不选,所的得到的序列长度。

    因为区间是固定的,并且发现一个区间内的答案可以通过两个子区间用某种方式进行转移。

    所以可以考虑到线段树做法。

    线段树中只需要维护两个值,一个是区间最大值,还有一个是区间序列长度(按照刚才的理解)的值。

    建树(甚至不用),修改,甚至不用$pushdown$,一切好说。

    但是发现$pushup$不好处理,显然两个子区间的值不能直接合并。

    所以一个结论——必须满足一定关系。

    可以发现,区间内的第一项一定在这个序列内,区间最大值也一定在这个序列内。

    对于要被$pushup$的区间,它的两个子区间已经处理好了,容易知道,左儿子区间内的序列每一项一定都在这个大区间内。

    因为前面形态固定,又不能选择不看到。

    所以只需要处理右儿子区间和左儿子区间最大值的关系,即可递归处理$len$值。

    递归要传入该区间的值必须大于的最小值,设为$v$。

    对于开始进入时,也就是左儿子的最大值。

    1. 如果$l==r$,该位置的值大于$v$,$return 1$,否则$return 0$。
    2. 将该区间劈成$lson,rson$左右两段区间,之后继续分类讨论:
    • 如果$lson$的最大值小于等于$v$,那么$lson$必然不会对答案产生贡献,去找$rson$。
    • 如果$lson$的最大值大于$v$,那么$rson$中剩下的在$lson,rson$组成的原区间中做贡献的项一定能贡献到最终答案中。

    即$+LEN(rt)-LEN(lson)$,这里注意,不是$LEN(lson)$,因为可能在$LEN(lson)$中存在的项,不一定在$LEN(rt)$这个大区间中出现。

    所以这两个值是完全不同的概念。

    之后再去寻找$lson$。

    然后就没了。

    细节见代码。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define LSON rt<<1
    #define RSON rt<<1|1
    #define DATA(x) a[x].data
    #define LEN(x) a[x].len
    #define LSIDE(x) a[x].l
    #define RSIDE(x) a[x].r
    #define MAXN 100010
    using namespace std;
    int n,m;
    double val[MAXN];
    struct Segment_Tree{
        double data;
        int len,l,r;
    }a[MAXN<<2];
    inline int read(){
        int date=0,w=1;char c=0;
        while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
        while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
        return date*w;
    }
    int solve(double v,int rt){
        if(v>=DATA(rt))return 0;
        if(val[LSIDE(rt)]>v)return LEN(rt);
        if(LSIDE(rt)==RSIDE(rt))return val[LSIDE(rt)]>v;
        int mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(DATA(LSON)<=v)return solve(v,RSON);
        else return solve(v,LSON)+LEN(rt)-LEN(LSON);
    }
    inline void pushup(int rt){
        DATA(rt)=max(DATA(LSON),DATA(RSON));
        LEN(rt)=LEN(LSON)+solve(DATA(LSON),RSON);
    }
    void buildtree(int l,int r,int rt){
        LSIDE(rt)=l;RSIDE(rt)=r;DATA(rt)=LEN(rt)=0;
        if(l==r)return;
        int mid=l+r>>1;
        buildtree(l,mid,LSON);
        buildtree(mid+1,r,RSON);
    }
    void update(int l,int r,double c,int rt){
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
            DATA(rt)=(double)c*1.00/l;
            LEN(rt)=1;
            return;
        }
        int mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)update(l,r,c,LSON);
        if(mid<r)update(l,r,c,RSON);
        pushup(rt);
    }
    void work(){
        int x,y;
        while(m--){
            x=read();y=read();
            val[x]=(double)y*1.00/x;
            update(x,x,y,1);
            printf("%d
    ",LEN(1));
        }
    }
    void init(){
        n=read();m=read();
        buildtree(1,n,1);
    }
    int main(){
        init();
        work();
        return 0;
    }
    
  • 相关阅读:
    通过连接池和字段索引,提升单点登录cas的性能
    crc16.c
    modbus.c
    sciencesoftware科学软件
    C++ ASSERT() 断言机制
    sessionKey
    main函数中argc理解
    compile,build和execute的区别
    Linux vi 中移动光标 命令
    OCP读书笔记(2)
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9520163.html
Copyright © 2011-2022 走看看