楼房重建

18 篇文章 0 订阅
6 篇文章 0 订阅

楼房重建
我 们 发 现 能 不 能 看 到 一 件 楼 房 就 要 看 前 面 有 没 有 比 它 斜 率 大 的 楼 房 我们发现能不能看到一件楼房就要看前面有没有比它斜率大的楼房
而 看 数 据 范 围 , 是 可 以 存 斜 率 的 , 所 以 很 自 然 地 分 治 一 下 ( 类 似 线 段 树 ) 而看数据范围,是可以存斜率的,所以很自然地分治一下(类似线段树) (线)
算 出 左 边 看 过 去 可 以 看 到 的 楼 房 , 算 出 右 边 可 以 看 到 的 楼 房 , 然 后 合 并 一 下 算出左边看过去可以看到的楼房,算出右边可以看到的楼房,然后合并一下
具 体 的 , 我 们 要 算 出 [ l , r ] ( 树 上 标 号 为 p ) , 则 a n s [ p < < 1 ] + 右 边 可 以 加 进 的 答 案 , 而 u p p d a t e 操 作 就 是 如 果 i d 比 m i d 小 就 改 左 边 , 否 则 改 右 边 具体的,我们要算出[l,r](树上标号为p),则ans[p<<1]+右边可以加进的答案,而uppdate操作就是如果id比mid小就改左边,否则改右边 [l,r]pans[p<<1]+uppdateidmid
实 际 上 , 这 里 维 护 了 一 个 单 调 栈 ( 不 同 之 处 在 于 这 个 单 调 栈 可 以 带 修 ) , 并 把 左 右 两 端 的 单 调 栈 合 并 , 合 并 就 是 左 边 单 调 , 右 边 求 出 可 加 入 总 单 调 栈 的 值 即 可 , 实际上,这里维护了一个单调栈(不同之处在于这个单调栈可以带修),并把左右两端的单调栈合并,合并就是左边单调,右边求出可加入总单调栈的值即可, ,
因 为 我 们 并 不 关 心 单 调 站 中 每 个 元 素 具 体 是 什 么 , 所 以 我 们 不 需 要 管 每 个 元 素 具 体 加 到 的 位 置 是 哪 里 , 我 们 只 需 要 维 护 最 大 值 和 栈 的 长 度 就 可 以 计 算 答 案 了 因为我们并不关心单调站中每个元素具体是什么,所以我们不需要管每个元素具体加到的位置是哪里,我们只需要维护最大值和栈的长度就可以计算答案了
所 以 就 可 以 合 并 了 所以就可以合并了

void uppdate(int l,int r,int p,int id,double val){
	if(l==r&&r==id){
		maxn[p]=val;ans[p]=1;//推到底直接更新
		return;
	}
	int mid=(l+r)>>1;
	if(id<=mid){uppdate(l,mid,p<<1,id,val);}
	else uppdate(mid+1,r,p<<1|1,id,val);//根据id修改
	maxn[p]=max(maxn[p<<1],maxn[p<<1|1]);//最大值就是左右两边的最大值
	ans[p]=ans[p<<1]+query(mid+1,r,p<<1|1,maxn[p<<1]); //左边的肯定可以看见,右边需要查哪些可以合并,并加上限制maxn[p<<1]
}

u p p d a t e 后 再 合 并 uppdate后再合并 uppdate
具 体 地 , 如 果 m a x n [ p < < 1 ] < = m x , 所 以 左 边 不 可 能 会 有 可 以 看 得 见 的 , 所 以 只 用 查 找 右 边 , 如 果 m a x n [ p < < 1 ∣ 1 ] > m x , 所 以 左 边 有 一 些 可 以 , 但 右 边 也 有 一 些 可 以 , 由 于 这 时 推 到 p 这 层 , 所 以 左 右 子 树 都 已 经 跟 新 完 , 所 以 a n s [ k ] = a n s [ k < < 1 ] + a n s [ k < < 1 ∣ 1 ] , 我 们 发 现 由 于 对 于 右 子 树 所 有 取 值 的 限 制 还 是 左 边 的 最 大 值 , 所 以 虽 然 限 制 发 生 变 化 , 但 不 大 于 左 子 树 的 最 大 值 , 所 以 对 于 右 边 来 说 , 限 制 不 变 , 还 是 左 子 树 的 最 大 值 , 所 以 右 边 可 以 的 还 是 可 以 , 不 可 以 的 还 是 不 可 以 具体地,如果maxn[p<<1]<=mx,所以左边不可能会有可以看得见的,所以只用查找右边,如果maxn[p<<1|1]>mx,所以左边有一些可以,但右边也有一些可以,由于这时推到p这层,所以左右子树都已经跟新完,所以ans[k]=ans[k<<1]+ans[k<<1|1],我们发现由于对于右子树所有取值的限制还是左边的最大值,所以虽然限制发生变化,但不大于左子树的最大值,所以对于右边来说,限制不变,还是左子树的最大值,所以右边可以的还是可以,不可以的还是不可以 maxn[p<<1]<=mx,maxn[p<<11]>mx,pans[k]=ans[k<<1]+ans[k<<11],

int query(int l,int r,int p,double mx){/*mx 是double型的*/
	if(maxn[p]<=mx){return 0;}
	if(l==r){return maxn[p]>mx;}
	int mid=(l+r)>>1,anss;
	if(maxn[p<<1]<=mx){anss=query(mid+1,r,p<<1|1,mx);}
	else anss=query(l,mid,p<<1,mx)+ans[p]-ans[p<<1];
	return anss;
}

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,ans[N*4];
double maxn[N*4];
int query(int l,int r,int p,double mx){/*mx 是double型的*/
	if(maxn[p]<=mx){return 0;}
	if(l==r){return maxn[p]>mx;}
	int mid=(l+r)>>1,anss;
	if(maxn[p<<1]<=mx){anss=query(mid+1,r,p<<1|1,mx);}
	else anss=query(l,mid,p<<1,mx)+ans[p]-ans[p<<1];
	return anss;
}
void uppdate(int l,int r,int p,int id,double val){
	if(l==r&&r==id){
		maxn[p]=val;ans[p]=1;
		return;
	}
	int mid=(l+r)>>1;
	if(id<=mid){uppdate(l,mid,p<<1,id,val);}
	else uppdate(mid+1,r,p<<1|1,id,val);
	maxn[p]=max(maxn[p<<1],maxn[p<<1|1]);
	ans[p]=ans[p<<1]+query(mid+1,r,p<<1|1,maxn[p<<1]); 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
    	int x,y;
    	scanf("%d%d",&x,&y);
    	uppdate(1,n,1,x,(double)y/x);/*注意不要写double(y/x)*/
    	printf("%d\n",ans[1]);
    }
}

double 的写法要注意

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值