首页 > 其他分享 >线段树中递归

线段树中递归

时间:2022-10-31 16:46:28浏览次数:79  
标签:递归 rs 楼房 线段 len mxl ls 区间 树中

楼房重建

题目描述

小 A 的楼房外有一大片施工工地,工地上有 \(N\) 栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。

为了简化问题,我们考虑这些事件发生在一个二维平面上。小 A 在平面上 \((0,0)\) 点的位置,第 \(i\) 栋楼房可以用一条连接 \((i,0)\) 和 \((i,H_i)\) 的线段表示,其中 \(H_i\) 为第 \(i\) 栋楼房的高度。如果这栋楼房上任何一个高度大于 \(0\) 的点与 \((0,0)\) 的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。

施工队的建造总共进行了 \(M\) 天。初始时,所有楼房都还没有开始建造,它们的高度均为 \(0\)。在第 \(i\) 天,建筑队将会将横坐标为 \(X_i\) 的房屋的高度变为 \(Y_i\)(高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。请你帮小 A 数数每天在建筑队完工之后,他能看到多少栋楼房?

输入格式

第一行两个正整数 \(N,M\)。

接下来 \(M\) 行,每行两个正整数 \(X_i,Y_i\)。

输出格式

\(M\) 行,第 \(i\) 行一个整数表示第 \(i\) 天过后小 A 能看到的楼房有多少栋。

样例 #1

样例输入 #1

3 4
2 4
3 6
1 1000000000
1 1

样例输出 #1

1
1
1
2

提示

对于 \(100\%\) 的数据,\(1 \le X_i \le N\),\(1 \le Y_i \le 10^9\),\(1\le N,M \le 10^5\)。


题解

根据题意可以看出要把\((x,y)\)转成斜率\(k\)
所以只要\(k_1 ~k_{i-1}<k_i\) 那么就可以看到第\(i\)个建筑
抽象一下这个题就是求有单点修改的最大上升子序列(即在最上层的那个上升子序列)
用线段树维护某个区间内的高度最大值\(mx\)和最大上升子序列的长度\(len\)。
修改高度的时候就是普通的线段树的操作
但是更新\(len\)即合并两个子区间比较难
一个区间\(ls , rs\)的信息是已知的(修改时会递归的到线段树的叶子节点而叶子节点的信息已知从而可以推出其他节点的信息)
\(ls\)前面没有楼挡着所以它的\(len\)不会改变直接贡献给父节点
所以只用求\(rs\)的\(len\)因为\(rs\)会被\(ls\)挡住所以要求的是\(L :\) \(rs\)中大于\(mxl\)的最大上升子序列。(\(maxl:\)左子区间\(k\)最大值)
设\(get(mxl,rs)\)为求解\(L\)的函数
父节点的\(len\)即为\(len_{ls}+ get(mxl,rs)\)
在\(get(mxl,rs)\)中可以递归求解\(rs\)中大于\(mxl\)的最大上升子序列。
设\(lss\)为右区间的左子区间,\(rss\)为右区间的右子区间

  1. 若右区间的左子区间最大值大于等于h,那么它的右子区间的个数就可以直接求出来(即\(len_{rs}-len_{lss}+get(mxl,lss))\),继续递归\(lss\)即可。
  2. 若右区间的左子区间最大值小于h,那么他的左子区间的贡献必为0,所以继续递归右子区间即可即\((get(mxl,rss)))\)

总的时间复杂度\(O(nlogn^2)\)

std

#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
using namespace std;
const int N = 1e5+2;
struct tree
{
	double mx;
	int len;
	#define mx(x) t[x].mx
	#define len(x) t[x].len
}t[N<<2];
int n,m;
double a[N];
int get(double maxl,int p,int l,int r)
{
	if(mx(p)<maxl)return 0;
	if(a[l]>maxl) return len(p);
	int mid = (l+r)>>1;
	if(mx(ls)<=maxl) return get(maxl,rs,mid+1,r);
	else return get(maxl,ls,l,mid) + len(p)-len(ls);
}

void change(int p,int l,int r,int x,int v)
{
	
	if(l==r)
	{
		mx(p) = (double)v/x;
		len(p) = 1;
		return;
	}
	int mid = (l+r)>>1;
	if(x<=mid)change(ls,l,mid,x,v);
	else if(x>mid) change(rs,mid+1,r,x,v);
	
	mx(p)= max(mx(ls),mx(rs));
	len(p) = len(ls)+get(mx(ls),rs,mid+1,r);
	
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		a[x] = (double)y/x;
		change(1,1,n,x,y);
		printf("%d\n",len(1));
		
	}
	return 0;
}

标签:递归,rs,楼房,线段,len,mxl,ls,区间,树中
From: https://www.cnblogs.com/AC7-/p/16844868.html

相关文章

  • 【XSY4191】sequence(分块,线段树)
    题面sequence题解考虑把原序列每\(k\)位分成一段,然后对于每一段维护起点在这一段中的最小值。先考虑询问,对于起点在中间的整段我们直接线段树查区间最小值,现在考虑两......
  • 【XSY4041】搬砖(线段树)
    题面搬砖题解题意为求最大的\(p\)使得\(h_1\equivh_2\equiv\cdots\equivh_n\pmodp\)。即\(h_2-h_1\equivh_3-h_2\equiv\cdots\equivh_n-h_{n-1}\equiv0\pm......
  • P1803 凌乱的yyy / 线段覆盖
    题目背景快noip了,yyy很紧张!题目描述现在各大oj上有 nn 个比赛,每个比赛的开始、结束的时间点是知道的。yyy认为,参加越多的比赛,noip就能考的越好(假的)。所以......
  • 递归算法之跳水板
    题目:你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可......
  • 递归输出一桌淘小子年龄
    #include<stdio.h>intage(intn){ intc; if(n==1){ c=10; }else{ c=age(n-1)+2; } returnc; }intmain(){ intage(intn); printf("age(5)=%d\n......
  • 递归方法求5!
    #include<stdio.h>longf(intn){ if(n==1){ return1; }else{ returnn*f(n-1); }}intmain(){ intm=5; longf(intn); printf("5!=%d\n",f(m))......
  • 【XSY3810】公路建设(线段树,kruskal)
    题面题解一开始竟然没反应过来是最小生成树。考虑用线段树直接维护每个区间的答案。注意到一个区间最多只有\(n-1\)条树边有用,所以线段树每个节点用一个vector按权......
  • 【XSY3551】Inserting Lines(线段树)
    题意:数轴上有无穷个格子,每个坐标上各有一个格子,你需要支持以下操作共\(n\)次:在\(x=k\)这个格子前插入一个格子,并将所有\(x\geqk\)的格子后移一位。时间++。询问......
  • 【XSY3549】Tree(线段树,换根)
    原题不想说(太懒了),就说一下总结到的两点想法?对于树上多次询问路径信息的问题,如果两条路径的信息无法快速合并(即不能pushup),但是在路径两端增加/删除单点后的信息变化可以......
  • 基本线段树
    线段树P3372【模板】线段树1已知一个数列ai,有下列两种操作将区间[x,y]内每个数加上k求区间[x,y]中每个数的和线段树的思想便是将数列a,用若干个区间,在树上用结点表......