首先你要知道题问的是什么:
使用一种数据结构,动态地维护以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);
}
}