zoukankan      html  css  js  c++  java
  • 【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)与楼房(_{1sim i-1})无交点,就是视线(_i)的斜率大于视线(_{1sim 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)=egin{cases}f(rt)&l=r且x<Mx(rt)\0&l=r且xge Mx(rt)\Calc(x,l,mid,lc)+f(rt)-f(lc)&l≠r且x<Mx(lc)\Calc(x,mid+1,r,rc)&l≠r且xge 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
    ",S.Qry());//线段树上存储斜率
    	return 0;
    }
    
  • 相关阅读:
    新年第一个项目思考
    ICP通讯(EN)
    将绝对路径变成URL路径(原创)
    [图像处理] 铁路轨道道岔检测(一)
    一个外国的好网站 http://www.ilovejackdaniels.com/
    内容全面的企业网站策划书
    asp.net2.0学习历程 菜鸟到中级程序员的飞跃
    TimeSpan 用法 求离最近发表时间的函数
    ASP.NE导出Excel
    在网页中,鼠标放在Flash链接上,光标形状会在手型和鼠标之间不停的闪 的解决办法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2957.html
Copyright © 2011-2022 走看看