题解 楼房重建
题面
解析
这题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; }