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; }