洛谷 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); } }
众人皆醉我独醒,举世皆浊我独清