题目:BZOJ3745/LOJ2809.
题目大意:给定一个长度为
n
n
n的序列
a
i
a_i
ai,求:
∑
l
=
1
n
∑
r
=
l
n
(
r
−
l
+
1
)
max
i
=
l
r
{
a
i
}
min
i
=
l
r
{
a
i
}
  
(
m
o
d
  
1
0
9
)
\sum_{l=1}^{n}\sum_{r=l}^{n}(r-l+1)\max_{i=l}^{r}\{a_i\}\min_{i=l}^{r}\{a_i\}\,\,(mod\,\,10^9)
l=1∑nr=l∑n(r−l+1)i=lmaxr{ai}i=lminr{ai}(mod109)
1 ≤ n ≤ 5 ∗ 1 0 5 , 1 ≤ a i ≤ 1 0 8 1\leq n\leq 5*10^5,1\leq a_i\leq 10^8 1≤n≤5∗105,1≤ai≤108,答案对 1 0 9 10^9 109取模.
分治做法.
首先,看到这种求所有子区间的 m i n min min和 m a x max max运算一下的套路题,马上就能知道它是分治 / / /线段树+单调栈.
考虑线段树+单调栈的做法,按照套路大力枚举右端点,单调栈维护每一个最值相同的区间,同时用线段树维护每个左端点的贡献.
这个时候我们的线段树应该需要同时维护信息 ( r − l + 1 ) (r-l+1) (r−l+1)的区间加法,两个最值的区间修改,并要同时维护询问它们的乘积之和,这并不能用线段树直接维护.
考虑如何维护区间 ( r − l + 1 ) (r-l+1) (r−l+1)的信息,我们发现枚举右端点为 i i i时,左端点 1 1 1的贡献为 i i i, 2 2 2的贡献为 i − 1 i-1 i−1直到 i i i的贡献为 1 1 1,这提示我们可以在一开始分两棵线段树维护.设 m x i mx_i mxi表示位置 i i i的最值乘积,那么一棵维护 m x i mx_i mxi,另一棵维护 m x i ( n − i + 1 ) mx_i(n-i+1) mxi(n−i+1),查询的时候就可以用第二棵线段树在区间 [ 1 , i ] [1,i] [1,i]上的总贡献减去 ( n − i ) (n-i) (n−i)乘上第一棵线段树在区间 [ 1 , i ] [1,i] [1,i]上的总贡献.
解决这个问题后考虑最值的维护,用单调栈的套路需要支持区间乘法和区间除法,但是这里的模数不是素数,无法做除法.不过我们注意到每一个需要除法的区间必然都是经过乘法后的撤销操作,所以给每个区间打两个永久化标记即可.
由于两棵树上的极值标记其实是一样的,所以我们不需要真的开两棵线段树,只需要同时在一棵线段树上维护四个标记就可以了,实测这样做可以减小一半的常数.
时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
代码如下(注:下面这份代码在BZOJ上不可过):
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500000,mod=1000000000;
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int mul(int a,int b){return (LL)a*b%mod;}
void sadd(int &a,int b){a=add(a,b);}
void ssub(int &a,int b){a=sub(a,b);}
void smul(int &a,int b){a=mul(a,b);}
int n,a[N+9];
int tr[N*4+9][4];
void Pushup(int k){
int ls=k<<1,rs=k<<1|1,vl=mul(tr[ls][0],tr[ls][1]),vr=mul(tr[rs][0],tr[rs][1]);
tr[k][2]=add(mul(tr[ls][2],vl),mul(tr[rs][2],vr));
tr[k][3]=add(mul(tr[ls][3],vl),mul(tr[rs][3],vr));
}
void Build(int L,int R,int k){
tr[k][0]=tr[k][1]=1;
if (L==R) return;
int mid=L+R>>1;
Build(L,mid,k<<1);Build(mid+1,R,k<<1|1);
}
void Change(int L,int R,int id,int v,int l,int r,int k){
if (L==l&&R==r){
if (id==2) tr[k][3]=n-l+1;
tr[k][id]=v;
return;
}
int mid=l+r>>1;
if (R<=mid) Change(L,R,id,v,l,mid,k<<1);
else if (L>mid) Change(L,R,id,v,mid+1,r,k<<1|1);
else Change(L,mid,id,v,l,mid,k<<1),Change(mid+1,R,id,v,mid+1,r,k<<1|1);
Pushup(k);
}
int mx[N+9],mn[N+9],cmx,cmn;
int ans;
Abigail into(){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
}
Abigail work(){
Build(1,n,1);
for (int i=1;i<=n;++i){
Change(i,i,2,1,1,n,1);
for (;cmx&&a[mx[cmx]]<=a[i];--cmx)
Change(mx[cmx-1]+1,mx[cmx],0,1,1,n,1);
Change(mx[cmx]+1,i,0,a[i],1,n,1);
mx[++cmx]=i;
for (;cmn&&a[mn[cmn]]>=a[i];--cmn)
Change(mn[cmn-1]+1,mn[cmn],1,1,1,n,1);
Change(mn[cmn]+1,i,1,a[i],1,n,1);
mn[++cmn]=i;
sadd(ans,mul(sub(tr[1][3],mul(tr[1][2],n-i)),mul(tr[1][0],tr[1][1])));
}
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}