洛谷4198 楼房重建(线段树)
神仙题。pb 说是套路题???
首先只有楼房顶有用,然后要斜率递增。
没事干了,上线段树。
肯定要记录区间单调队列的长度 \(len\)。当然把整个能看到的位置都记下来会更方便,然而复杂度就爆了。
顺便再来个区间最大值 \(mx\)。
修改是单点,叶子很好搞。询问就是根节点的 \(len\)。
难点就在 pushup。
\(mx\) 不管,只看 \(len\)。
首先发现,最左边的和最大值都能看到。
pushup 时,首先左边的所有数都仍然能被看到,右边能看到的数的个数和左边的最大值有关,右边留下的只有大于左边最大值的数。
所以我们递归 pushup:
参数有:线段树节点编号 \(o\) 和能看到的数的下界 \(lwr\)(也就是只保留 \(>lwr\) 的数,然后再求看不看得到)
如果 \(mx_o\le lwr\),整个区间都不能看到。
如果 \(a_l>lwr\),那么答案就是 \(len_o\)(能看到的数和没有这个下界没区别)。
如果 \(mx_{lson}\le lwr\),那么左儿子全都没有,递归到右儿子 \(pushup(rson,lwr)\)。
否则左儿子的最大值肯定保留了下来,那么右儿子能看到的数与没有下界没有区别,个数是 \(len_o-len_{lson}\)。递归到左儿子 \(pushup(lson,lwr)+len_o-len_{lson}\)。
这样 pushup 一次是 \(O(\log n)\)。
时间复杂度 \(O(m\log^2n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=400040;
#define ls o<<1
#define rs o<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
template<typename T>
inline void read(T &x){
x=0;
char ch=getchar();bool f=false;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
int n,m,len[MAXN];
double a[MAXN],mx[MAXN];
inline void pushup1(int o){
mx[o]=max(mx[ls],mx[rs]);
}
int pushup2(int o,int l,int r,double lwr){
if(a[l]>lwr) return len[o];
if(mx[o]<=lwr) return 0;
int mid=(l+r)>>1;
if(mx[ls]<=lwr) return pushup2(rson,lwr);
else return pushup2(lson,lwr)+len[o]-len[ls];
}
inline void pushup(int o,int l,int r){
pushup1(o);
int mid=(l+r)>>1;
len[o]=len[ls]+pushup2(rson,mx[ls]);
}
void update(int o,int l,int r,int p,double v){
if(l==r) return void((mx[o]=v,len[o]=1));
int mid=(l+r)>>1;
if(mid>=p) update(lson,p,v);
if(mid<p) update(rson,p,v);
pushup(o,l,r);
}
int main(){
read(n);read(m);
while(m--){
int x,y;
read(x);read(y);
update(1,1,n,x,a[x]=1.0*y/x);
printf("%d\n",len[1]);
}
return 0;
}