BZOJ2957 楼房重建 线段树

题意:给定N个初始长度均为0,坐标为1-N的向y轴正坐标延伸的线段。给定M个操作,每个操作将横坐标为x的线段长度变为y,求每次操作以后(0,0)与(i,yi)的连线不被其他线段所阻挡的线段的数量。

题解:显然对于所有不被阻挡的线段,其斜率一定是单调递增的,由于每次修改只会影响到后面的线段能不能看到(当然还有这个线段修改之后看不到了),因此我们用线段树维护一个区间斜率的最大值,显然对于一个节点,左儿子斜率的最大值>右儿子斜率的最大值,那右区间便不能计入答案。如果yi>[x,N]斜率的最大值,后面那一段显然废掉了;否则,递归询问两个子节点,看是否需要将两个子区间废掉。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN=100000+2;
typedef struct NODE{
    int c,l,r;
    NODE *lchild,*rchild;
    double rate;
    NODE(){}
    NODE(int _l,int _r):l(_l),r(_r),c(0),rate(0){}
} *TREE;
TREE root;
int N,M;

void Build(TREE &x,int l,int r){
    x=new NODE(l,r);
    if(l==r) return;

    int m=(l+r)>>1;
    Build(x->lchild,l,m),Build(x->rchild,m+1,r);
}

int Query(TREE &x,double rate){
    if(x->l==x->r) return x->rate>rate;
    if(x->lchild->rate<=rate) return Query(x->rchild,rate);
    return x->c-x->lchild->c+Query(x->lchild,rate);
}

void Update(TREE &x,int p,double rate){
    if(x->l==x->r){
        x->c=1,x->rate=rate;
        return;
    }

    int m=(x->l+x->r)>>1;
    if(p<=m) Update(x->lchild,p,rate);
    else Update(x->rchild,p,rate);

    x->rate=max(x->lchild->rate,x->rchild->rate),x->c=x->lchild->c+Query(x->rchild,x->lchild->rate);
}

int main(){
    cin >> N >> M;
    Build(root,1,N);

    for(int i=1,x,y;i<=M;i++){
        scanf("%d %d",&x,&y);
        Update(root,x,(double)y/x);
        cout << root->c << endl;
    }

    return 0;
}
View Code

 

posted @ 2017-02-26 13:04  WDZRMPCBIT  阅读(67)  评论(0编辑  收藏