【洛谷P5654】基础函数练习题
线段树
题目大意:
给定一个排列 $p$ 和一个数列 $w$。
定义函数 $f(l,r)$ 为:
其中 $m$ 为 $p$ 的区间 $[l,r]$ 中的最大值的下标。
每次询问给出 $l,r$,求 $f(l,r)$ 的值。
题解:
考虑对原序列,找到其 $m$ 的位置,然后分割成左右两个子序列,对子序列继续执行这样的分割操作。最后我们会得到一棵树。
考虑对于一个询问 $[l,r]$,我们找到其 $m$ 的位置,则该询问在这个结点分为 $f(l,m-1)$ 和 $f(m+1,r)$。
这个类似于线段树上查询的过程,但是本题中这样建出来的树的层数是 $O(n)$ 的,复杂度不对。
本题的询问都是不带修改的,因此我们还有一个思想,对于一个表示 $[l,r]$ 的结点,其分隔点为 $m$,我们想办法计算 $f(i,m-1),i\in[l,m-1]$ 和 $f(m+1,i),i\in[m+1,r]$,然后对于一个询问,我们只需得到左、右两个信息就可以计算答案了。
这个可以逐层向上更新,用线段树维护答案即可。
对于区间 $[l,r]$(假设为一个结点的左儿子,即其父亲的分割点为 $r+1$。右儿子同理),其线段树上的 $l\sim m-1$ 位置的答案为右端点为 $m-1$ 时的答案,而 $m+1\sim r$ 位置,对应的右端点为 $r+1$。
这个时候,我们把 $l\sim m-1$ 的值,同时和 $f(m+1,r)$ 取 $\max$,然后加上 $w_m$ 的值就完成更新。
左右都同理可得。
用线段树实现区间对一个数取 $\max$,区间加,单点查询即可。
至于求 $[l,r]$ 的 $m$ 的位置,用 ST 表维护区间最值即可。
时间复杂度 $O((n+q)\log n)$。
Code:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define lg2(x)(31-__builtin_clz(x))
typedef long long LL;
const int N=5e5+6;
const LL inf=1e18;
int n,m,a[N],p[N],st[19][N],pos[N];
inline int ask(int l,int r){
const int lg=lg2(r-l+1);
return max(st[lg][l],st[lg][r-(1<<lg)+1]);
}
struct que{
int l,r,id;
};
vector<que>vec[N];
LL ans[N];
struct sgt{
LL mx[N<<2],add[N<<2];
inline void pushdown(int o){
const int ls=o<<1,rs=o<<1|1;
if(mx[o]!=-inf){
LL&x=mx[o];
mx[ls]=max(mx[ls],x-add[ls]);
mx[rs]=max(mx[rs],x-add[rs]);
x=-inf;
}
add[ls]+=add[o],add[rs]+=add[o],add[o]=0;
}
void init(int l,int r,int o){
mx[o]=-inf*(l<r);
if(l<r){
const int mid=l+r>>1;
init(l,mid,o<<1),init(mid+1,r,o<<1|1);
}
}
void Add(int l,int r,int o,const int&L,const int&R,const LL&dlt){
if(L<=l&&r<=R)add[o]+=dlt;else{
pushdown(o);
const int mid=l+r>>1;
if(L<=mid)Add(l,mid,o<<1,L,R,dlt);
if(mid<R)Add(mid+1,r,o<<1|1,L,R,dlt);
}
}
void Max(int l,int r,int o,const int&L,const int&R,const LL&tag){
if(L<=l&&r<=R)mx[o]=max(mx[o],tag-add[o]);else{
pushdown(o);
const int mid=l+r>>1;
if(L<=mid)Max(l,mid,o<<1,L,R,tag);
if(mid<R)Max(mid+1,r,o<<1|1,L,R,tag);
}
}
LL ask(int l,int r,int o,const int&pos){
if(l==r)return mx[o]+add[o];
pushdown(o);
const int mid=l+r>>1;
if(pos<=mid)return ask(l,mid,o<<1,pos);
return ask(mid+1,r,o<<1|1,pos);
}
}A,B;
void build(int l,int r){
if(l>r)return;
const int mid=pos[ask(l,r)];
build(l,mid-1),build(mid+1,r);
for(int i=0;i<vec[mid].size();++i){
int l=vec[mid][i].l,r=vec[mid][i].r,id=vec[mid][i].id;
ans[id]=max(A.ask(1,n,1,l),B.ask(1,n,1,r))+a[mid];
}
LL x=A.ask(1,n,1,mid+1);
A.Max(1,n,1,l,mid,x),A.Add(1,n,1,l,mid,a[mid]);
x=B.ask(1,n,1,mid-1);
B.Max(1,n,1,mid,r,x),B.Add(1,n,1,mid,r,a[mid]);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>p[i],st[0][i]=p[i],pos[p[i]]=i;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<19;++i)
for(int j=1;j+(1<<i-1)<=n;++j)
st[i][j]=max(st[i-1][j],st[i-1][j+(1<<i-1)]);
for(int i=1;i<=m;++i){
int l,r;
cin>>l>>r;
vec[pos[ask(l,r)]].push_back((que){l,r,i});
}
A.init(1,n,1),B.init(1,n,1);
build(1,n);
for(int i=1;i<=m;++i)
cout<<ans[i]<<'\n';
return 0;
}