题目大意
定义一个序列的 average 为
最
大
值
+
最
小
值
2
\frac{最大值+最小值}{2}
2最大值+最小值。
给定一个序列
a
1
,
⋯
,
a
n
a_1,\cdots,a_n
a1,⋯,an,有
m
m
m 次询问,每次问这个区间的所有子区间的 average 期望。
n
,
m
≤
2
×
1
0
5
,
1
≤
a
i
≤
1
0
9
n,m \le 2 \times 10^5,\ 1 \le a_i \le 10^9
n,m≤2×105, 1≤ai≤109
多测,
∑
n
,
∑
m
≤
3
×
1
0
5
\sum n,\sum m \le 3 \times 10^5
∑n,∑m≤3×105
8s
\\
\\
\\
扯淡
考场上脑补了个
O
(
m
n
)
O(m \sqrt n)
O(mn) 的只增莫队巨难写,写到最后又 WA 又 T。
第二天牛爷爷说这是个原题,上网搜了一下大家说这个题是【HNOI2016 序列】,于是就去学习了一下,老年选手被这个神奇的技巧秀得头皮发麻。。。
题解
首先 E [ max + min 2 ] = E [ max ] + E [ min ] 2 \mathbb E[\frac{\max+\min}{2}]=\frac{\mathbb E[\max]+\mathbb E[\min]}{2} E[2max+min]=2E[max]+E[min],所以问题变成每次求一个区间所有子区间的 max \max max 和以及 min \min min 和。这就是【HNOI2016 序列】了。
做法多种多样,莫队和在线
O
(
n
log
n
+
m
)
O(n \log n+m)
O(nlogn+m) 的都有,但其实本质相同的,莫队到在线也就多一步小转化而已。下面就讲在线的。
考虑这样一个数组:
f
i
f_i
fi 表示右端点为
i
i
i、左端点
∈
[
1
,
i
]
\in [1,i]
∈[1,i] 的所有区间的
max
\max
max 和。它的转移很简单,就是找到
i
i
i 上一个比它大的数
L
i
L_i
Li,那么左端点
≤
L
i
\le L_i
≤Li 的区间的
max
\max
max 都保持不变,左端点
∈
(
L
i
,
i
]
\in (L_i,i]
∈(Li,i] 的区间的
max
\max
max 等于
a
i
a_i
ai,因此
f
i
=
f
L
i
+
a
i
(
i
−
L
i
)
f_i=f_{L_i}+a_i(i-L_i)
fi=fLi+ai(i−Li)
这东西怎么用呢?变形得到
f
i
−
f
L
i
=
a
i
(
i
−
L
i
)
f_i-f_{L_i}=a_i(i-L_i)
fi−fLi=ai(i−Li),也就是说,知道了
i
i
i 的转移点
L
i
L_i
Li,那么就知道了左端点
∈
(
L
i
,
i
]
\in(L_i,i]
∈(Li,i]、右端点为
i
i
i 的所有区间的
max
\max
max 和。
更进一步,
i
i
i 从
L
i
L_i
Li 转移来,
L
i
L_i
Li 从
L
L
i
L_{L_i}
LLi 转移来……这样形成一个转移路径(实际上就是以
i
i
i 结尾的单调栈),在这条路径上的任何一个
j
j
j,都满足
f
i
−
f
j
f_i-f_j
fi−fj 等于左端点
∈
(
j
,
i
]
\in(j,i]
∈(j,i]、右端点为
i
i
i 的所有区间的
max
\max
max 和。
接下来就可以做这题了。
询问一个区间
[
l
,
r
]
[l,r]
[l,r] 的所有子区间的
max
\max
max 和,先找到这个区间的最大值所在位置
m
x
mx
mx,那么凡是左端点
∈
[
l
,
m
x
]
\in [l,mx]
∈[l,mx]、右端点
∈
(
m
x
,
r
]
\in (mx,r]
∈(mx,r] 的子区间,最大值都是
a
m
x
a_{mx}
amx。因此问题转化成
[
l
,
m
x
)
[l,mx)
[l,mx)、
(
m
x
,
r
]
(mx,r]
(mx,r] 的子问题。
考虑
(
m
x
,
r
]
(mx,r]
(mx,r],重要的性质是,
m
x
mx
mx 一定在
r
r
r 的转移路径上,因此
f
r
−
f
m
x
f_r-f_{mx}
fr−fmx 就是左端点
∈
(
m
x
,
r
]
\in (mx,r]
∈(mx,r]、右端点为
r
r
r 的子区间的
max
\max
max 和;同理,
m
x
mx
mx 一定也在
r
−
1
r-1
r−1 的转移路径上,所以
f
r
−
1
−
f
m
x
f_{r-1}-f_{mx}
fr−1−fmx 就是左端点
∈
(
m
x
,
r
−
1
]
\in (mx,r-1]
∈(mx,r−1]、右端点为
r
−
1
r-1
r−1 的子区间的
max
\max
max 和……
因此
(
m
x
,
r
]
(mx,r]
(mx,r] 的贡献就是
(
f
r
−
f
m
x
)
+
(
f
r
−
1
−
f
m
x
)
+
⋯
+
(
f
m
x
+
1
−
f
m
x
)
=
(
∑
i
=
m
x
+
1
r
f
i
)
−
f
m
x
(
r
−
m
x
)
(f_r-f_{mx})+(f_{r-1}-f_{mx})+\cdots+(f_{mx+1}-f_{mx})=\left(\sum_{i=mx+1}^rf_i\right)-f_{mx}(r-mx)
(fr−fmx)+(fr−1−fmx)+⋯+(fmx+1−fmx)=(i=mx+1∑rfi)−fmx(r−mx)
所以求个
f
f
f 的前缀和就做好了。
同理可求
[
l
,
m
x
)
[l,mx)
[l,mx) 的贡献,以及
min
\min
min 和。
时间复杂度,预处理 rmq 需要 O ( n log n ) O(n \log n) O(nlogn),预处理 f f f 需要 O ( n ) O(n) O(n),每个询问是 O ( 1 ) O(1) O(1) 的,因此是 O ( n log n + m ) O(n \log n + m) O(nlogn+m)。
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int maxn=2e5+5, MX=17;
const LL mo=1e9+7, inv2=(mo+1)>>1;
int n,m,a[maxn];
int st_mx[MX+2][maxn],st_mn[MX+2][maxn],Log[maxn];
void rmq_pre() {
fo(i,1,n) st_mx[0][i]=st_mn[0][i]=i;
fo(i,2,n) Log[i]=Log[i>>1]+1;
fo(j,1,MX)
fo(i,1,n) {
st_mx[j][i]=st_mx[j-1][i];
st_mn[j][i]=st_mn[j-1][i];
int t=i+(1<<(j-1));
if (t<=n) {
if (a[st_mx[j][i]]<a[st_mx[j-1][t]]) st_mx[j][i]=st_mx[j-1][t];
if (a[st_mn[j][i]]>=a[st_mn[j-1][t]]) st_mn[j][i]=st_mn[j-1][t];
}
}
}
pair<int,int> rmq(int l,int r) {
int t=Log[r-l+1];
int mx=(a[st_mx[t][l]]>=a[st_mx[t][r-(1<<t)+1]]) ?st_mx[t][l] :st_mx[t][r-(1<<t)+1];
int mn=(a[st_mn[t][l]]<a[st_mn[t][r-(1<<t)+1]]) ?st_mn[t][l] :st_mn[t][r-(1<<t)+1];
return make_pair(mx,mn);
}
LL f_l_mx[maxn],f_l_mn[maxn],f_r_mx[maxn],f_r_mn[maxn];
LL s_l_mx[maxn],s_l_mn[maxn],s_r_mx[maxn],s_r_mn[maxn];
int z0,z[maxn];
void f_pre() {
z0=0;
fo(i,1,n) {
while (z0 && a[z[z0]]<a[i]) z0--;
f_l_mx[i]=(f_l_mx[z[z0]]+(LL)a[i]*(i-z[z0]))%mo;
s_l_mx[i]=(s_l_mx[i-1]+f_l_mx[i])%mo;
z[++z0]=i;
}
z0=0;
fo(i,1,n) {
while (z0 && a[z[z0]]>=a[i]) z0--;
f_l_mn[i]=(f_l_mn[z[z0]]+(LL)a[i]*(i-z[z0]))%mo;
s_l_mn[i]=(s_l_mn[i-1]+f_l_mn[i])%mo;
z[++z0]=i;
}
z0=0;
s_r_mx[n+1]=0;
fd(i,n,1) {
while (z0 && a[z[z0]]<=a[i]) z0--;
f_r_mx[i]=(f_r_mx[z[z0]]+(LL)a[i]*(z[z0]-i))%mo;
s_r_mx[i]=(s_r_mx[i+1]+f_r_mx[i])%mo;
z[++z0]=i;
}
z0=0;
s_r_mn[n+1]=0;
fd(i,n,1) {
while (z0 && a[z[z0]]>a[i]) z0--;
f_r_mn[i]=(f_r_mn[z[z0]]+(LL)a[i]*(z[z0]-i))%mo;
s_r_mn[i]=(s_r_mn[i+1]+f_r_mn[i])%mo;
z[++z0]=i;
}
}
LL Pow(LL x,LL y) {
LL re=1;
for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;
return re;
}
LL sum(LL x) {return x*(x+1)%mo*inv2%mo;}
int main() {
int T;
scanf("%d",&T);
while (T--) {
scanf("%d %d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]);
rmq_pre();
f_pre();
while (m--) {
int l,r;
scanf("%d %d",&l,&r);
pair<int,int> p=rmq(l,r);
int mx=p.first, mn=p.second;
LL ans_mx=a[mx]*(LL)(mx-l+1)%mo*(r-mx+1)%mo;
(ans_mx+=s_l_mx[r]-s_l_mx[mx]+mo-f_l_mx[mx]*(r-mx)%mo+mo)%=mo;
(ans_mx+=s_r_mx[l]-s_r_mx[mx]+mo-f_r_mx[mx]*(mx-l)%mo+mo)%=mo;
LL ans_mn=a[mn]*(LL)(mn-l+1)%mo*(r-mn+1)%mo;
(ans_mn+=s_l_mn[r]-s_l_mn[mn]+mo-f_l_mn[mn]*(r-mn)%mo+mo)%=mo;
(ans_mn+=s_r_mn[l]-s_r_mn[mn]+mo-f_r_mn[mn]*(mn-l)%mo+mo)%=mo;
LL ans=(ans_mx+ans_mn)%mo*inv2%mo*Pow(sum(r-l+1),mo-2)%mo;
printf("%lld\n",ans);
}
}
}