cf1827B2
1827B2 Range Sorting(Hard Version) ##link
description
对于一个长度为 \(n\) 且不存在相同元素的序列 $ a_1...a_n (1 \leq n \leq 3e5)$ ,定义美丽值为将其排为有序序列的最小时间花费,排序过程中可以进行任意次如下操作:
每次将一个区间[L,R]
排为有序,时间花费R-L
求给定数组的所有子数组(在原数组连续)的美丽值之和
solution
- 首先考虑给一个区间
[L,R]
排序的时候,我们进行的操作区间肯定不会重合,否则我们直接选合并后的区间进行一次操作肯定更优 - 所以我们给
[L,R]
排序就相当于将其分为若干段,给每段操作排序一次即可(可能存在长度为1的段,该段时间花费为0) - 显然想让时间花费最少就要尽量分多段,区间
[L,R]
的美丽值为R-L-(段数-1)
- 假设\(L\leq k<R\),发现只有 \(max\{a_{L},...a_k\} < min\{a_{k+1}...a_R\}\) ,才能让 \(k\) 作为一段的结束,所以该区间美丽值为
R-L-(符合条件的k的个数)
- 现在考虑要求给定序列的所有子数组的美丽值之和,显然可以考虑每个点作为符合条件的
k
对应的L,R
的情况数,但是这样即使预处理st表,复杂度也是 \(n^2\) 的 - 所以我们考虑对于 \(i (2\leq i \leq n)\) ,当 \(min\{a_{k+1}...a_R\} = a_i\)时,我们去找对应的
(L,k,R)
的数量 - \(k\) 只能取 \(i\) 左边第一个满足 \(a_k < a_i\) 的位置
- 令 \(x\) 为 \(k\) 左边第一个满足 \(a_x > a_i\) 的位置, \(L\) 只能在 \([x+1,k]\) 之间取值
- 类似的,令 \(y\) 为 \(i\) 右边第一个满足 \(a_y < a_i\) 的位置, \(R\) 只能在 \([i,y-1]\) 之间取
- 所以当前 \(i\) 对于答案的贡献是 \(-(k-x)*(y-i)\)
- 最后答案再加上对于所有子数组的 \(R-L\) 即可
查询每个数左边或者右边第一个小于自己的数可以单调栈 \(\mathcal{O}(n)\) 预处理
查询左边第一个大于某个特定数可以线段树上二分 单次 \(\mathcal{O}(logn)\) 在线查询
code
cf1827B2
#include<bits/stdc++.h>
#define ll long long
#define L (rt<<1)
#define R (rt<<1|1)
using namespace std;
const int N=3e5+10;
int n,a[N],sta[N],tp,lx[N],rx[N];
int mx[N*4];
ll ans;
void build(int rt,int l,int r)
{
if(l==r) return mx[rt]=a[l],void();
int mid=(l+r)/2;
build(L,l,mid),build(R,mid+1,r);
mx[rt]=max(mx[L],mx[R]);
}
int Find(int rt,int l,int r,int p,int v)
{
if(r<=p&&mx[rt]<=v) return -1;
if(l==r) return l;
int mid=(l+r)/2;
if(p<=mid) return Find(L,l,mid,p,v);
int tmp=Find(R,mid+1,r,p,v);
if(tmp!=-1) return tmp;
return Find(L,l,mid,p,v);
}
int main()
{
// freopen("1.in","r",stdin);
int cs; cin>>cs;
while(cs--)
{
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
a[0]=0,sta[tp=1]=0;
for(int i=1;i<=n;++i)
{
while(tp&&a[sta[tp]]>a[i]) --tp;
lx[i]=sta[tp],sta[++tp]=i;
}
a[n+1]=0,sta[tp=1]=n+1;
for(int i=n;i>=1;--i)
{
while(tp&&a[sta[tp]]>a[i]) --tp;
rx[i]=sta[tp],sta[++tp]=i;
}
a[0]=(int)1e9+1;
build(1,0,n);
ans=0;
for(int i=2;i<=n;++i)
{
int k=lx[i];
if(k==0) continue;
int x=Find(1,0,n,k-1,a[i]),y=rx[i];
ans-=1ll*(k-x)*(y-i);
}
for(int i=1;i<n;++i) ans+=1ll*i*(n-i);
printf("%lld\n",ans);
}
return 0;
}