题解 楼房重建

题面

 

解析

这题dalao们用线段树可以轻松切掉。。。

然而,本蒟蒻却喜欢用神(bao)奇(li)的数据结构,

于是,我们来讲一下分块做法。(顺便说一下对于某些数据可能得吸氧qwq)

首先,分析题意,

看得见一栋楼房,也就是(0,0)与这栋楼房的顶点的连线的斜率比之前的所有楼房高。

因此,看得见的楼房数也就是斜率递增序列的长度,

另外,计算长度时必须要把每栋楼房都算进去(也就是说并不是最长上升子序列

那么,我们只需要在每个块中维护一个上升序列,

对于修改,也是直接暴力。

具体做法(看不懂可以看后面完整代码):

inline void reset(int x)/*寻找上升序列*/{
    bl[x].top=0;
    for(int i=(x-1)*gap+1;i<=x*gap;i++){
        if(cmp(i,bl[x].stack[bl[x].top])) bl[x].stack[++bl[x].top]=i;
    }
}

 

在询问时二分查找每个块中第一个能与之前所有块中的序列构成上升序列的元素,

那么上升序列中它后面的所有元素就都可以加在答案中了。

具体做法:

inline void work()/*统计答案*/{
    int ans=0,tem=0;
    for(int i=1;i<=b[n];i++)/*二分找每个块*/{
        int l=1,r=bl[i].top;
        int ret=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(cmp(bl[i].stack[mid],tem)){r=mid-1;ret=mid; }
            else l=mid+1;
        }
        if(ret) ans+=bl[i].top-ret+1,tem=bl[i].stack[bl[i].top];
    }
    printf("%d\n",ans);
}

 

 

具体看完整代码吧:

#include <iostream>
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;
 
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
 
struct block{
    int top;//序列的长度
    int stack[404];//名字不重要
}bl[404];//块的信息
int n,m,gap;
int a[100001]/*高度*/,b[100001]/**/;
 
bool cmp(int x,int y)/*比较斜率*/{
    if(!y) return a[x]>0;
    return (ll)x*a[y]<(ll)y*a[x];//避免精度误差将除法转换成乘法
}
 
inline void reset(int x)/*寻找上升序列*/{
    bl[x].top=0;
    for(int i=(x-1)*gap+1;i<=x*gap;i++){
        if(cmp(i,bl[x].stack[bl[x].top])) bl[x].stack[++bl[x].top]=i;
    }
}
 
inline void work()/*统计答案*/{
    int ans=0,tem=0;
    for(int i=1;i<=b[n];i++)/*二分找每个块*/{
        int l=1,r=bl[i].top;
        int ret=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(cmp(bl[i].stack[mid],tem)){r=mid-1;ret=mid; }
            else l=mid+1;
        }
        if(ret) ans+=bl[i].top-ret+1,tem=bl[i].stack[bl[i].top];
    }
    printf("%d\n",ans);
}
 
int main(){
    n=read();m=read();
    gap=sqrt(n);
    for(int i=1;i<=n;i++) b[i]=(i-1)/gap+1;
    while(m--){        
        int p=read();
        a[p]=read();
        reset(b[p]);
        work();
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/zsq259/p/10566951.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值