Luogu4198 楼房重建(线段树妙用)
Luogu4198 楼房重建(线段树妙用)
快快乐乐切题,开开心心抄题解
一眼能看出来这是两个\(log\)的做法,但是你永远也想不到第二个放在哪里了
我们从题意得知,我们要求从一开始的极长上升序列
于是如果没有修改的话,我们扫一遍就可以了
然而有修改之后,我们不会做了
但是我们仍然知道要用线段树维护
我们开始思考如何\(pushup\),发现不能直接做,因为不满足可加性
然而,可以满足的是,如果当前区间的最小值满足,那么整个区间都满足
这个性质虽然不会运用到代码中,但是对正解有极大地启发作用
第二只\(log\)在\(pushup\)中......
我们对于每一个区间维护一个以左端点开头,斜率递增的最大长度,以及这个最大长度的最后一个斜率
于是我们在\(pushup\)的时候,左区间的长度肯定全要了
再看右区间的最大值合法还是不合法,不合法的话,那就没有右区间的事了,合法的话,我们就开始向下递归
于是我们看递归时当前区间的左区间的最大值,如果满足,那就接着向左区间递归,把右区间的长度全加上
不满足的话,那就去右区间找就行了
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
return s*t;
}
const int N=1e5+5;
int n,m;
struct XDS{
#define ls x<<1
#define rs x<<1|1
int len[N*4],mxx[N*4],mxy[N*4];
int get(int x,int mx,int my,int l,int r){
if(mx*mxy[x]<=mxx[x]*my)return 0;
if(l==r)return 1;int mid=l+r>>1;
if(mx*mxy[ls]<=mxx[ls]*my)return get(rs,mx,my,mid+1,r);
return get(ls,mx,my,l,mid)+len[x]-len[ls];
}
void pushup(int x,int l,int r){
len[x]=len[ls];mxx[x]=mxx[ls];mxy[x]=mxy[ls];
if(!len[ls]){mxx[x]=mxx[rs];mxy[x]=mxy[rs];len[x]=len[rs];return ;}
if(mxx[ls]*mxy[rs]<=mxx[rs]*mxy[ls]||!len[rs])return ;
mxx[x]=mxx[rs];mxy[x]=mxy[rs];int mid=l+r>>1;
// cout<<x<<" "<<l<<" "<<r<<" "<<mxx[ls]<<" "<<mxy[ls]<<endl;
len[x]+=get(rs,mxx[ls],mxy[ls],mid+1,r);
}
void ins(int x,int l,int r,int pos,int my){
if(l==r){
mxx[x]=l;mxy[x]=my;
if(!my)len[x]=0;
else len[x]=1;
return ;
}
int mid=l+r>>1;
if(pos<=mid)ins(ls,l,mid,pos,my);
else ins(rs,mid+1,r,pos,my);
pushup(x,l,r);return ;
}
#undef ls
#undef rs
}xds;
signed main(){
n=read();m=read();
while(m--){
int x=read(),y=read();
xds.ins(1,1,n,x,y);
printf("%lld\n",xds.len[1]);
}
}
QQ:2953174821