线段树 bzoj2957 楼房重建
大概意思就是求序列从一位置开始的动态上升序列。
分块可过,但这一类题目其实可用线段树。
也就是维护每个区间的上升序列长度。
下面这种求法只是用于当前节点所覆盖的区间完全被查询区间覆盖。
具体而言,查询时如果左儿子的max值< K,只去右儿子找。如果max_lc>=K,那么右儿子的长度全部符合,只要再递归着找左儿子即可。
因为这道题要求的区间是1~n,所以是很裸的题。。。
主体函数
int q(double k,int x)
{
if(t[x].l==t[x].r)return k<t[x].h;
if(t[x*2].h<k)return q(k,x*2+1);
else return q(k,x*2)+t[x].sz-t[x*2].sz;
}
void up(int x)
{
t[x].h=max(t[x*2].h,t[x*2+1].h);
t[x].sz=t[x*2].sz+q(t[x*2].h,x*2+1);
}
void C(int l,double k,int x)
{
if(t[x].l==t[x].r){t[x].sz=1;t[x].h=k;return;}
int mid=t[x].l+t[x].r>>1;
if(l<=mid)C(l,k,x*2);
else C(l,k,x*2+1);
up(x);
}
那么,对于求给定区间的呢
其实就加了一个地方,只要递归到某个节点被询问区间完全覆盖即可,但要按照顺序枚举,并不断更新当前的最大高度,并累加答案。
多的地方
int q(int k,int x)
{
if(t[x].l==t[x].r){return k<t[x].h;}
int mid=t[x].l+t[x].r>>1;
if(t[x*2].h<k)return q(k,x*2+1);
else return q(k,x*2)+t[x].szr-t[x*2].szr;
}
inline void Q_(int l,int r,int x)
{
if(t[x].l>=l&&t[x].r<=r)
{h+=q(mh,x);mh=max(mh,t[x].h);return;}
int mid=t[x].l+t[x].r>>1;
if(l<=mid)Q_(l,r,x*2);
if(r>mid)Q_(l,r,x*2+1);
}
inline int Q(int l,int k)
{
h=1;mh=k;Q_(1,l,1);
return h;
}
这道题的完整代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define N 100005
using namespace std;
struct tree
{
int l,r,sz;double h;
}t[N*4];
int n,m;
void build(int l,int r,int x)
{
t[x].l=l;t[x].r=r;
if(l==r){t[x].sz=0;t[x].h=0.0;return;}
int mid=l+r>>1;
build(l,mid,x*2);
build(mid+1,r,x*2+1);
}
int q(double k,int x)
{
if(t[x].l==t[x].r)return k<t[x].h;
if(t[x*2].h<k)return q(k,x*2+1);
else return q(k,x*2)+t[x].sz-t[x*2].sz;
}
void up(int x)
{
t[x].h=max(t[x*2].h,t[x*2+1].h);
t[x].sz=t[x*2].sz+q(t[x*2].h,x*2+1);
}
void C(int l,double k,int x)
{
if(t[x].l==t[x].r){t[x].sz=1;t[x].h=k;return;}
int mid=t[x].l+t[x].r>>1;
if(l<=mid)C(l,k,x*2);
else C(l,k,x*2+1);
up(x);
}
int main()
{
scanf("%d%d",&n,&m);
build(1,n,1);int x,y;
while(m--)
{
scanf("%d%d",&x,&y);
C(x,(double)y/x,1);
printf("%d\n",q(0,1));
}
}