luogu P4198 楼房重建

这道题就是用线段树维护一个斜率"强制"递增序列元素个数

其他的都不必多说,主要难点在于在pushup时怎样更新个数

当时我脑残就直接用vector保存下序列,然后二分更新,实测只有10分

其实完全不必记录序列,只要把区间内最大值记录下来即可

但这样面临的问题就是没有办法二分,于是通过看题解便想到了递归处理

具体来说增加一个count函数,用来找一个区间内的满足条件的序列元素个数

最初只需进入右区间,count(p*2+1,mx[p*2]),同时要满足限制必须大于左区间的最大值;

分几种情况:

  当l(p)==r(p)时,递归入边界,return mx(p)>k; (k是最开始传入的mx[p*2]);

  当mx(p*2)<=k时,只需进入右区间, return count(p*2+1,k);

  else return len(p)-len(p*2)+count(p*2,k); (当mx[p*2]>k时,右区间内都满足条件,进入左区间递归,注意考虑左区间的遮挡,而不是len(p*2+1) );

太难了,想不到

上代码:

#include
#include
using namespace std;
int n,m;
struct Node{
    int l,r,len;
    double mx;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define len(x) t[x].len
    #define mx(x) t[x].mx
}t[400010];
void build(int p,int l,int r){
    l(p)=l;r(p)=r;
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    build(p*2,l,mid);build(p*2+1,mid+1,r);
}
int count(int p,double k){
    if(l(p)==r(p)) return mx(p)>k;
    if(mx(p*2)<=k) return count(p*2+1,k);
    else return len(p)-len(p*2)+count(p*2,k);
}
void change(int p,int x,double k){
    if(l(p)==r(p)){
        mx(p)=k;len(p)=1;
        return;
    }
    int mid=(l(p)+r(p))>>1;
    if(x<=mid) change(p*2,x,k);
    else change(p*2+1,x,k);
    mx(p)=max(mx(p*2),mx(p*2+1));
    len(p)=len(p*2)+count(p*2+1,mx(p*2));
}
int main(){
    scanf("%d%d",&n,&m);
    build(1,1,n);
    while(m--){
        double x,y;scanf("%lf%lf",&x,&y);double tan=y/x;
        change(1,x,tan);printf("%d\n",len(1));
    }
    return 0;
}

 

你可能感兴趣的:(luogu P4198 楼房重建)