楼房重建 与 线段树前缀最大值
楼房重建 与 线段树前缀最大值
P4198 楼房重建
先来看原题:P4198 楼房重建
设 \(s_i=\frac{H_i}{i}\),即斜率。
本质是要求 \(\max_{i=1}^{j-1}s_i\le s_j\) 的个数。
由于带单点修改,我们考虑在线段树上维护这个信息。
对线段树上每个节点 \(x\) 代表的区间 \([l,r]\) 维护两个值:
区间最大的 \(s_i\) 为 \(mx_x\),只考虑当前区间内时上述式子的答案 \(f_x\)。
最后的答案即 \(f_{root}\)。
前者是基本操作。考虑后者。
后者不能简单地把两个子区间的答案加起来,因为没有考虑左区间对右区间的贡献。
于是我们用一个递归函数 \(calc(x,l,r,pre)\) 来计算右区间的答案。
其表示线段树上编号为 \(x\) 的点,其代表区间为 \([l,r]\),区间内大于 \(pre\) 的答案。
则 \(f_x=f_{ls}+calc(rs,mid+1,r,mx_{ls})\)。
解释:第一行和第二行显然,考虑第三行和第四行。
第三行当 \(pre\) 小于左区间时,\(pre\) 对右区间没有贡献,于是直接加上右区间的贡献即 \(f_x-f_{ls}\),然后递归处理左区间。
第四行当 \(pre\) 不小于左区间时,左区间答案为 0,于是直接递归右区间。
这样就能在 \(O(\log n)\) 内 \(PushUp\) 一个节点,修改一个值就是 \(O(\log ^2n)\) 的。
#include<bits/stdc++.h>
using namespace std;
int n,m;
#define ld long double
const int N=1e5+5;
struct tree{
#define ls (x<<1)
#define rs (ls|1)
#define mid ((l+r)>>1)
ld mx[N*4]; int f[N*4];
int calc(int x,int l,int r,ld pre){
if(l==r){
if(pre<mx[x])return f[x];
return 0;
}
if(pre<mx[ls])return calc(ls,l,mid,pre)+f[x]-f[ls];
return calc(rs,mid+1,r,pre);
}
void update(int x,int l,int r,int y,ld z){
if(l==r){
mx[x]=z,f[x]=1;
return;
}
if(y<=mid)update(ls,l,mid,y,z);
else update(rs,mid+1,r,y,z);
mx[x]=max(mx[ls],mx[rs]);
f[x]=f[ls]+calc(rs,mid+1,r,mx[ls]);
}
}tr;
int main(){
// freopen("build.in","r",stdin),freopen("build.out","w",stdout);
ios::sync_with_stdio(0); cin.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
tr.update(1,1,n,x,(ld)y/x);
cout<<tr.f[1]<<"\n";
}
}
P4425 [HNOI/AHOI2018] 转盘
考虑确定起点时,等到最后一个出现,然后走一圈不停下收掉。
把环拉成 \(2n\) 的链,答案就是:
\(j-i\) 即 \(j\) 离起点的距离,\(n-1\) 是走一圈的花费。
令 \(a_i=T_i-i\),则
对于 \(j>i+n-1\) 的部分不会有影响,所以取 \(j\ge i\),这是一个后缀最大值的形式,用楼房重建的技巧。
时间复杂度 \(O(n\log ^2 n)\)。
#include<bits/stdc++.h>
using namespace std;
int n,m,p;
const int N=2e5+5;
int t[N];
struct tree{
#define ls (x<<1)
#define rs (ls|1)
#define mid ((l+r)>>1)
int mx[N*4],f[N*4],f1[N*4];
int calc(int x,int l,int r,int pre){
if(l==r){
return max(pre,mx[x])+l;
}
if(pre<mx[rs])return min(f1[x],calc(rs,mid+1,r,pre));
else return min(pre+mid+1,calc(ls,l,mid,pre));
}
void build(int x,int l,int r){
if(l==r){
mx[x]=t[l],f[x]=t[l]+l;
return;
}
build(ls,l,mid),build(rs,mid+1,r);
mx[x]=max(mx[ls],mx[rs]);
f[x]=min(f[rs],f1[x]=calc(ls,l,mid,mx[rs]));
}
void update(int x,int l,int r,int y,int z){
if(l==r){
mx[x]=z,f[x]=z+l;
return;
}
if(y<=mid)update(ls,l,mid,y,z);
else update(rs,mid+1,r,y,z);
mx[x]=max(mx[ls],mx[rs]);
f[x]=min(f[rs],f1[x]=calc(ls,l,mid,mx[rs]));
}
int query(int x,int l,int r,int L,int R,int mx1){
if(r<L||R<l)return 1e9;
if(L<=l&&r<=R){
return calc(x,l,r,mx1);
}
return min(query(ls,l,mid,L,R,max(mx1,mx[rs])),query(rs,mid+1,r,L,R,mx1));
}
}tr;
int lastans=0;
int main(){
// freopen("circle.in","r",stdin);
// freopen("circle.out","w",stdout);
ios::sync_with_stdio(0); cin.tie(0);
cin>>n>>m>>p;
for(int i=1;i<=n;i++){
cin>>t[i]; t[i]-=i;
t[i+n]=t[i]-n;
}
tr.build(1,1,2*n);
lastans=tr.query(1,1,2*n,1,n,-1e9)+n-1;
cout<<lastans<<"\n";
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
if(p)x^=lastans,y^=lastans;
tr.update(1,1,2*n,x,y-x);
tr.update(1,1,2*n,x+n,y-x-n);
lastans=tr.query(1,1,2*n,1,n,-1e9)+n-1;
cout<<lastans<<"\n";
}
}