洛谷 P4198 楼房重建 题解

题面

首先你要知道题问的是什么:
使用一种数据结构,动态地维护以1为起点地最长上升子序列(把楼房的高度转化成斜率地序列)的长度;

怎么做?线段树!

我们在线段树上维护两个东西:1.这个区间内斜率的最大值 2.从这段区间开头可以看到的区间内的所有楼房

初始化:
对于每一个叶子节点,从这段区间头可以看到的楼房数量一定为1,区间斜率最大值一定为该点的斜率;

在合并时:

1.我们可以先查找右区间的左区间的最大值,如果右区间的左区间的最大值比左区间的最大值小,那么右区间的左区间的所有答案一定看不到,所以我们就可以递归查找右区间的右区间

2.如果右区间的左区间的最大值比左区间的最大值大,那么原来被右区间的左区间挡住的现在一样会被挡住,我们就可以加上右区间的右区间的答案,所以我们可以递归查找右区间的左区间

时间复杂度是O(nlog^2n);

 

#include <bits/stdc++.h>
#define inc(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;
class segment{
	public:
	class node{
		public:
		double maxn;
		int tot;
	}tree[2000010];
	int query(int k,int l,int r,double goal){
		if(tree[k].maxn<=goal){
			return 0;
		}
		if(l==r){
			return tree[k].maxn>goal;
		}
		int mid=(l+r)/2;
		if(tree[k<<1].maxn<=goal){
			return query(k<<1|1,mid+1,r,goal);
		}
		else{
			return query(k<<1,l,mid,goal)+tree[k].tot-tree[k<<1].tot;
		}
	}
	void change(int k,int l,int r,int goal,double value){
		if(l==goal&&r==goal){
			tree[k].maxn=value;
			tree[k].tot=1;
			return;
		}
		int mid=(l+r)/2;
		if(goal<=mid) change(k<<1,l,mid,goal,value);
		else change(k<<1|1,mid+1,r,goal,value);
		tree[k].maxn=max(tree[k<<1].maxn,tree[k<<1|1].maxn);
		tree[k].tot=tree[k<<1].tot+query(k<<1|1,mid+1,r,tree[k<<1].maxn);
	}
}SEG;
int n,m;
template<class nT>
inline void read(nT& x){
	char c;
	while(c=getchar(),!isdigit(c));
	x=c^48;
	while(c=getchar(),isdigit(c)) x=x*10+c-48;
}
int main()
{
	read(n); read(m);
	inc(i,1,m){
		int x,y;
		read(x); read(y);
		SEG.change(1,1,n,x,(double)y/x);
		printf("%d\n",SEG.tree[1].tot);
	}
} 

 

 

 

posted @ 2019-11-03 21:06  神之右大臣  阅读(212)  评论(0编辑  收藏  举报