其他分享
首页 > 其他分享> > 【BZOJ2957】楼房重建(线段树)

【BZOJ2957】楼房重建(线段树)

作者:互联网

点此看题面

大致题意: 有\(n\)个楼房,第\(i\)个楼房可以表示为连结\((i,0)\)和\((i,H_i)\)的线段,且第\(i\)个楼房可以被看见当且仅当连结\((0,0)\)和\((i,H_i)\)的线段与表示其他楼房的线段无交点。每次单点修改\(H_i\),求每次修改后能看到几个楼房。

前言

\(Jan\ 29th\)刷题计划(6/6),算法标签:线段树。

终于完成了今天的计划了!

斜率

令楼房\(_i\)表示连结\((i,0)\)和\((i,H_i)\)的线段,视线\(_i\)表示连结\((0,0)\)和\((i,H_i)\)的线段。

考虑视线\(_i\)与楼房\(_{1\sim i-1}\)无交点,就是视线\(_i\)的斜率大于视线\(_{1\sim i-1}\)的斜率。而此处的斜率,即为\(\frac {H_i}i\)。

也就是说,题目要求的就是视线斜率的上升子序列的长度(注意,不是最长上升子序列)。

线段树

考虑用线段树进行维护。

对于线段树上每一个点,我们维护两个信息:这个点所代表的区间的最大值\(Mx\)上升子序列的长度\(f\)。则显然每次的答案就是根节点的\(f\)。

\(Mx\)的维护就是常规操作,而\(f\)的维护就比较有技巧了。

我们可以新定义一个函数\(Calc(x,l,r,rt)\),代表在\([l,r]\)这个区间里大于\(x\)的上升序列长度,则:

\[f(rt)=f(lc)+Calc(Mx(lc),mid+1,r,rc)\]

\[Calc(x,l,r,rt)=\begin{cases}f(rt)&l=r且x<Mx(rt)\\0&l=r且x\ge Mx(rt)\\Calc(x,l,mid,lc)+f(rt)-f(lc)&l≠r且x<Mx(lc)\\Calc(x,mid+1,r,rc)&l≠r且x\ge Mx(lc)\end{cases}\]

这样我们就可以在\(O(logN)\)的时间复杂度内实现\(PushUp()\),因而总复杂度是\(O(Nlog^2N)\)的。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define DB double
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,m;
class SegmentTree
{
    private:
        #define PT CI l=1,CI r=n,CI rt=1
        #define LT l,mid,rt<<1
        #define RT mid+1,r,rt<<1|1
        #define PU(x) (O[x]=node(O[x<<1].f+Calc(O[x<<1].Mx,RT),max(O[x<<1].Mx,O[x<<1|1].Mx)))//注意上升子序列长度的上传
        struct node {int f;DB Mx;I node(CI p=0,Con DB& t=0):f(p),Mx(t){}}O[N<<2];
    public:
        I void Upt(CI x,Con DB& y,PT)//单点修改
        {
            if(l==r) return (void)(O[rt]=node(1,y));int mid=l+r>>1;
            x<=mid?Upt(x,y,LT):Upt(x,y,RT),PU(rt);
        }
        I int Calc(Con DB& x,PT)//求出区间内大于x的上升序列的长度
        {
            if(l==r) return x<O[rt].Mx?O[rt].f:0;int mid=l+r>>1;//特判边界
            return x<O[rt<<1].Mx?Calc(x,LT)+O[rt].f-O[rt<<1].f:Calc(x,RT);//递归求解
        }
        I int Qry() {return O[1].f;}//整个序列的答案
}S;
int main()
{
    RI i,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i)
        scanf("%d%d",&x,&y),S.Upt(x,1.0*y/x),printf("%d\n",S.Qry());//线段树上存储斜率
    return 0;
}

标签:rt,CI,楼房,线段,BZOJ2957,斜率,define
来源: https://www.cnblogs.com/chenxiaoran666/p/BZOJ2957.html