题目描述
求:
∑
i
=
1
n
∑
j
=
i
n
(
j
−
i
+
1
)
m
i
n
(
a
i
,
a
i
+
1
,
…
a
j
)
m
a
x
(
a
i
,
a
i
+
1
,
…
a
j
)
\sum_{i=1}^{n}\sum_{j=i}^{n} (j-i+1)min(a_i,a_{i+1}, \dots a_j)\ max(a_i,a_{i+1}, \dots a_j)
i=1∑nj=i∑n(j−i+1)min(ai,ai+1,…aj) max(ai,ai+1,…aj)
Sol
经典的序列分治(CDQ分治)题 , 很有思考价值
算法主题自然是分治
把区间分成两半后 , 只考虑跨过中点的区间
我们需要一种能很好求出 m i n , m a x min,max min,max的方法
不妨先把情况分一下类:
- min 和 max 都在左边
- min 和 max都在右边
- min 在左而 max 在右
- max 在左而 min 在右
显然其实只有 min 和 max 是不是在同一侧的两种本质不同的情况 , 因为 min 和 max 是等价处理的
我们先考虑枚举一个左端点(或右端点) i i i
假设 min和max 在同一侧, 这个就很好办了 , 不管它在左在右 , 我们都可以维护一个单调指针来得到当前这一边的 min 和 max
由于不能和另一边产生冲突 , 另一边也要维护两个单调指针 , 这样就能找出使得 min max 都在同一侧的区间了 , 直接等差数列求和公式即可
而对于不在同一侧的 , 我们先写一下式子:
这里只讨论 min 在左而 max在右的情况 , 另一种一样的做法
a
n
s
[
i
]
=
∑
j
=
m
i
d
+
1
r
(
j
−
i
+
1
)
∗
m
i
n
(
a
[
i
]
∼
a
[
m
i
d
]
)
∗
m
a
x
(
a
[
m
i
d
+
1
]
∼
a
[
j
]
)
ans[i]=\sum_{j=mid+1}^r (j-i+1) * min(a[i]\sim a[mid]) *max(a[mid+1]\sim a[j])
ans[i]=j=mid+1∑r(j−i+1)∗min(a[i]∼a[mid])∗max(a[mid+1]∼a[j])
我们可以通过单调指针得出 和
i
i
i 有关的min , 记为
M
i
n
i
Min_i
Mini ,而后面的记为
M
a
x
j
Max_j
Maxj
a
n
s
[
i
]
=
M
i
n
i
∑
j
=
m
i
d
+
1
r
(
j
+
1
)
∗
M
a
x
j
−
i
∗
M
a
x
j
ans[i]=Min_i \sum_{j=mid+1}^{r} (j+1)*Max_j-i*Max_j
ans[i]=Minij=mid+1∑r(j+1)∗Maxj−i∗Maxj
对于每一个
i
i
i合法的该种情况的区间一定是单调且连续的
于是维护一下 ( j + 1 ) ∗ M a x j (j+1)*Max_j (j+1)∗Maxj和 M a x j Max_j Maxj 的后缀和就行了
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
typedef long long ll;
const int N=5e5+10;
const int mod=1e9;
int n,a[N],maxq[N],maxn[N],maxs[N],minn[N],minq[N],mins[N];
inline void upd(int &x,int y){x+=y;if(x>=mod) x-=mod;}
int ans=0;
inline ll SUM(int l,int r,int num){return 1ll*(l+r)*num/2%mod;}
void Divide(int l,int r)
{
if(l==r) return upd(ans,1ll*a[l]*a[l]%mod);
register int mid=l+r>>1;
Divide(l,mid);Divide(mid+1,r);
register int p=mid+1,q=mid+1;
register int Min=a[mid],Max=a[mid];
maxn[mid+1]=minn[mid+1]=a[mid+1];
for(register int i=mid+2;i<=r;++i) maxn[i]=max(maxn[i-1],a[i]),minn[i]=min(minn[i-1],a[i]);
maxq[r+1]=maxs[r+1]=minq[r+1]=mins[r+1]=0;
for(register int i=r;i>mid;--i) {
maxq[i]=mod-maxn[i];maxs[i]=1ll*(i+1)*maxn[i]%mod;
upd(maxq[i],maxq[i+1]);upd(maxs[i],maxs[i+1]);
minq[i]=mod-minn[i];mins[i]=1ll*(i+1)*minn[i]%mod;
upd(minq[i],minq[i+1]);upd(mins[i],mins[i+1]);
}
for(register int i=mid;i>=l;--i){
Min=min(Min,a[i]);Max=max(Max,a[i]);
while(p<=r&&a[p]<=Max) ++p;
while(q<=r&&a[q]>=Min) ++q;
register int pos=min(p,q)-1;
register ll D=1ll*Max*Min%mod;
upd(ans,D*SUM(mid-i+2,pos-i+1,pos-mid)%mod);
if(p<q) {
register ll S=1ll*Min*(((maxs[p]-maxs[q]+mod)%mod+1ll*i*(maxq[p]-maxq[q]+mod)%mod)%mod)%mod;
upd(ans,S);
}
else if(q<p){
register ll S=1ll*Max*(((mins[q]-mins[p]+mod)%mod+1ll*i*(minq[q]-minq[p]+mod)%mod)%mod)%mod;
upd(ans,S);
}
}
Min=a[mid+1],Max=a[mid+1];p=q=mid;
for(register int i=mid+1;i<=r;++i){
Min=min(Min,a[i]);Max=max(Max,a[i]);
while(p>=l&&a[p]<Max) --p;
while(q>=l&&a[q]>Min) --q;
register int pos=max(p,q)+1;
register ll D=1ll*Max*Min%mod;
upd(ans,D*SUM(i-mid+1,i-pos+1,mid+1-pos)%mod);
}
return;
}
int main()
{
n=read();
for(register int i=1;i<=n;++i) a[i]=read();
Divide(1,n);
printf("%d\n",ans);
}