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;
}
posted @ 2019-03-13 16:03  风浔凌  阅读(201)  评论(0编辑  收藏