• luogu题解P4198楼房重建--线段树神操作


    题目链接

    https://www.luogu.org/problemnew/show/P4198

    分析

    一句话题意,一条数轴上有若干楼房,坐标为(xi)的楼房有高度(hi),那么它的斜率为(hi/xi),操作包含单元素高度修改,动态询问最长上升斜率序列个数

    一开始想什么分治或是离线操作之类的,却因为水平低并不会做,看了题解居然发现就是线段树!看了一下感觉真妙啊,线段树真是个神奇的数据结构

    线段树维护两个东西(sum[now],mx[now]);

    (sum[now])(now)管辖区间([l,r])的最长上升斜率个数

    类似的(mx[now])就是那个区间最大斜率,也就是最长上升序列中最右边的

    我们假设更新操作递归到([L,R])区间,设(p)([L,mid])区间最大斜率
    (mx[now<<1])

    那么([L,R])区间的贡献((sum[now]))显然等于([L,mid])区间贡献((sum[now<<1])) (+) 统计([mid+1,R])区间中大于(p)的最长上升序列个数.

    那么怎么计算([mid+1,R])区间中的大于(p)的最长上升序列个数呢,这个我在代码中给出了详细的注释

    代码

    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <cctype>
    #include <cstring>
    #include <cmath>
    #define ll long long 
    #define ri register int 
    using std::min;
    using std::max;
    template <class T>inline void read(T &x){
    	x=0;int ne=0;char c;
    	while(!isdigit(c=getchar()))ne=c=='-';
    	x=c-48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    	x=ne?-x:x;return ;
    }
    const int maxn=100005;
    const int inf=0x7fffffff;
    int n,m,t;
    double dta,p;
    struct Segment_Tree{
    	int sum[maxn<<2];//指now管辖的区间内最长上升序列个数 
    	double mx[maxn<<2];//指now管辖区间内最大斜率 
    	int calc(int now,int l,int r){
    		if(l==r) return mx[now]>p;
    		int mid=(l+r)>>1;
    		if(mx[now<<1]<=p)return calc(now<<1|1,mid+1,r);//如果左区间的最大值小于p,那么左区间贡献为0,计算右区间贡献 
    		return sum[now]-sum[now<<1]+calc(now<<1,l,mid);
    		//如果左区间有贡献,那么右边的不用管,因为sum[now]中本来就是递归到这里时[l,r]区间内最长上升序列个数
    		//如果左区间已有了贡献,说明左区间存在大于p的值,那么右区间计入sum[now]中的斜率肯定也能对现在答案产生贡献
    		//但是左区间中有些对sum[now]产生贡献的斜率此时可能小于p,因此答案就是右区间贡献+递归处理左区间贡献 
    		//右区间贡献就是sum[now]-sum[now<<1] 
    	}
    	void update(int now,int l,int r){
    		if(l==r){
    			mx[now]=dta;
    			sum[now]=1;
    			return ;
    		}
    		int mid=(l+r)>>1;
    		if(t<=mid)update(now<<1,l,mid);
    		else update(now<<1|1,mid+1,r);
    		mx[now]=max(mx[now<<1],mx[now<<1|1]);
    		p=mx[now<<1];//左区间最大值 
    		sum[now]=sum[now<<1]+calc(now<<1|1,mid+1,r);//计算右区间贡献 
    		return ;
    	}
    }T;
    int main(){
        int x;
    	read(n),read(m);
    	while(m--){
    		read(t),read(x);
    		dta=(double)x/t;
    		T.update(1,1,n);
    		printf("%d
    ",T.sum[1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    原生ajax四部曲
    jquery ajax参数
    java String format占位符
    servlet是什么
    直接插入排序算法
    通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的--20135334赵阳林
    信息安全系统设计基础第一周学习总结
    信息安全系统设计基础实验五:通讯协议设计
    信息安全系统设计第二次&第四次实验
    信息安全系统设计基础第一次实验报告
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/9560934.html
Copyright © 2020-2023  润新知