BZOJ2957 楼房重建
题目链接:戳我
线段树qwqwq动态维护最长上升子序列的长度
对于一个区间,我们记录两个参数——\(ans\)表示这个区间里面的上升子序列的个数,\(k\)表示最大的斜率。
关键是怎么合并?肯定是左区间的ans+右区间在左区间最大斜率的限制下的上升子序列个数。
怎么计算右区间的那一部分?我们把右区间分成左子区间和右子区间。如果左子区间的最大斜率比当前限制斜率(即左区间最大斜率)大,右子区间相当于限制和原先一样,只要递归计算左子区间,再加上右子区间原本的贡献即可。(注意不是右子区间的sum,因为那个没有大于左边的限制,具体写法请见代码)。如果左子区间的最大斜率比限制斜率小,那左子区间就相当于全部都看不到了,所以递归计算右子区间即可qwqwq
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
#define MAXN 100010
int n,m;
struct Node{int l,r,sum;double maxx;}t[MAXN<<2];
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void build(int x,int l,int r)
{
t[x].l=l,t[x].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
}
inline int calc(int x,double k)
{
int l=t[x].l,r=t[x].r;
if(l==r)
{
if(t[x].maxx>k) return 1;
else return 0;
}
if(t[ls(x)].maxx<=k) return calc(rs(x),k);
else return calc(ls(x),k)+t[x].sum-t[ls(x)].sum;
}
inline void update(int x,int pos,double k)
{
int l=t[x].l,r=t[x].r;
if(l==r)
{
t[x].maxx=k;
t[x].sum=1;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) update(ls(x),pos,k);
else update(rs(x),pos,k);
t[x].maxx=max(t[ls(x)].maxx,t[rs(x)].maxx);
t[x].sum=t[ls(x)].sum+calc(rs(x),t[ls(x)].maxx);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
build(1,1,n);
for(int i=1;i<=m;i++)
{
int x,h;
scanf("%d%d",&x,&h);
double cur=1.0*h/x;
update(1,x,cur);
printf("%d\n",t[1].sum);
}
return 0;
}