P8037 [COCI2015-2016#7] Prokletnik 题解
题目描述
定义一个序列是魔法序列,当且仅当中间所有元素均在第一个和最后一个元素之间。
给定一个长为 \(n\) 的序列 \(a\) , \(q\) 次询问 \(a[l\sim r]\) 中魔法序列长度最大值。
数据范围
- \(1\le n,q\le 5\cdot 10^5,1\le a_i\le 10^9\) 。
- \(1\le l\le r\le n\) 。
时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{128MB}\) 。
分析
不妨只考虑左端点为最小值,右端点为最大值的魔法序列。(令 \(a_i\gets -a_i\) 再做一遍即可)
\(x_i\) 表示左端点为 \(i\) 时,右端点的最大值。线段树维护 \(x_i-i+1\) 的最大值。离线对 \(r\) 扫描线,查询 \([l,r]\) 区间 \(\max\) 即可。
\(a[l\sim r]\) 是魔法序列当且仅当 \(R_l\lt r,L_r\lt l\) 。其中 \(L_i\) 为左边第一个比 \(h_i\) 大的位置, \(R_i\) 为右边第一个比 \(h_i\) 小的位置,可以用单调栈预处理。
\(R_l\lt r\) 的限制相对容易刻画,可以看成这个点仅在区间 \([l,R_l-1]\) 中出现,扫到 \(R_l\) 时将其贡献删除即可。
\(L_r\lt l\) 的限制相当于给一个后缀赋值 \(x_i=r\) 。
由于线段树维护的不是 \(x_i\) 而是 \(x_i-i+1\) ,所以很可惜不能直接区间赋值。
但我们可以执行区间加,单调栈维护 \(x_i\) 相同的段,弹栈同时在线段树上减掉原来的贡献,最后加入一个完整的段。
每个段只会被删掉一次,均摊只有 \(\mathcal O(n)\) 次线段树上的操作。
注意已经删除的点仍然可以产生贡献(只是贡献不再更新),因此需要另开一个线段树存储已经删除的点的贡献。
时间复杂度 \(\mathcal O(n\log n)\) 。
#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=5e5+5,inf=1e9;
int l,n,q,r;
int a[maxn],x[maxn],st[maxn],res[maxn];
int L[maxn],R[maxn];
vector<int> del[maxn];
vector<pii> vec[maxn];
struct sgmt
{
int op;
struct node
{
int l,r,mx,add;
}f[maxn<<2];
void pushup(int p)
{
f[p].mx=max(f[ls].mx,f[rs].mx);
}
void pushadd(int p,int v)
{
f[p].mx+=v,f[p].add+=v;
}
void pushdown(int p)
{
if(!f[p].add) return ;
pushadd(ls,f[p].add),pushadd(rs,f[p].add),f[p].add=0;
}
void build(int p,int l,int r)
{
f[p].l=l,f[p].r=r,f[p].add=0;
if(l==r) return f[p].mx=op*(-l+1),void();
int mid=(l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(p);
}
void modify(int p,int l,int r,int v)
{
if(l<=f[p].l&&f[p].r<=r) return pushadd(p,v);
if(l>f[p].r||r<f[p].l) return ;
pushdown(p);
modify(ls,l,r,v);
modify(rs,l,r,v);
pushup(p);
}
int query(int p,int l,int r)
{
if(l<=f[p].l&&f[p].r<=r) return f[p].mx;
if(l>f[p].r||r<f[p].l) return -inf;
pushdown(p);
return max(query(ls,l,r),query(rs,l,r));
}
}t1,t2;
void chmax(int &x,int y)
{
if(x<=y) x=y;
}
void solve()
{
for(int i=1;i<=n;i++) del[i].clear();
t1.build(1,1,n),t2.build(1,1,n);
for(int i=1,top=0;i<=n;i++)
{
while(top&&a[st[top]]<=a[i]) top--;
L[i]=st[top]+1,st[++top]=i;
}
for(int i=n,top=0;i>=1;i--)
{
while(top&&a[st[top]]>=a[i]) top--;
R[i]=st[top],st[++top]=i;
if(top) del[R[i]].push_back(i);
}
for(int i=1,top=0;i<=n;i++)
{
for(auto p:del[i]) t2.modify(1,p,p,t1.query(1,p,p)),t1.modify(1,p,p,-inf);
int cur=i;
while(top&&st[top]>=L[i]) t1.modify(1,st[top],cur-1,-x[top]),cur=st[top--];
if(top) t1.modify(1,L[i],cur-1,-x[top]);
st[++top]=L[i],x[top]=i,t1.modify(1,L[i],i,i);
for(auto p:vec[i]) chmax(res[p.se],max(t1.query(1,p.fi,i),t2.query(1,p.fi,i)));
}
}
int main()
{
scanf("%d",&n),t1.op=1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++) scanf("%d%d",&l,&r),vec[r].push_back(mp(l,i));
solve();
for(int i=1;i<=n;i++) a[i]=inf-a[i];
solve();
for(int i=1;i<=q;i++) printf("%d\n",res[i]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17102954.html
浙公网安备 33010602011771号