【2021 Multi-University 4 E】Didn‘t I Say to Make My Abilities Average in the Next Life?! 题解

题目大意

  定义一个序列的 average 为 $\frac{最大值+最小值}{2}$。
  给定一个序列 $a_1,\cdots,a_n$,有 $m$ 次询问,每次问这个区间的所有子区间的 average 期望。

  $n,m \le 2 \times 10^5,\ 1 \le a_i \le 10^9$
  多测,$\sum n,\sum m \le 3 \times 10^5$
  8s

扯淡

  考场上脑补了个 $O(m \sqrt n)$ 的只增莫队巨难写,写到最后又 WA 又 T。
  第二天牛爷爷说这是个原题,上网搜了一下大家说这个题是【HNOI2016 序列】,于是就去学习了一下,老年选手被这个神奇的技巧秀得头皮发麻。。。

题解

  首先 $\mathbb E[\frac{\max+\min}{2}]=\frac{\mathbb E[\max]+\mathbb E[\min]}{2}$,所以问题变成每次求一个区间所有子区间的 $\max$ 和以及 $\min$ 和。这就是【HNOI2016 序列】了。

  做法多种多样,莫队和在线 $O(n \log n+m)$ 的都有,但其实本质相同的,莫队到在线也就多一步小转化而已。下面就讲在线的。
  考虑这样一个数组:$f_i$ 表示右端点为 $i$、左端点 $\in [1,i]$ 的所有区间的 $\max$ 和。它的转移很简单,就是找到 $i$ 上一个比它大的数 $L_i$,那么左端点 $\le L_i$ 的区间的 $\max$ 都保持不变,左端点 $\in (L_i,i]$ 的区间的 $\max$ 等于 $a_i$,因此

  这东西怎么用呢?变形得到 $f_i-f_{L_i}=a_i(i-L_i)$,也就是说,知道了 $i$ 的转移点 $L_i$,那么就知道了左端点 $\in(L_i,i]$、右端点为 $i$ 的所有区间的 $\max$ 和。
  更进一步,$i$ 从 $L_i$ 转移来,$L_i$ 从 $L_{L_i}$ 转移来……这样形成一个转移路径(实际上就是以 $i$ 结尾的单调栈),在这条路径上的任何一个 $j$,都满足 $f_i-f_j$ 等于左端点 $\in(j,i]$、右端点为 $i$ 的所有区间的 $\max$ 和。

  接下来就可以做这题了。
  询问一个区间 $[l,r]$ 的所有子区间的 $\max$ 和,先找到这个区间的最大值所在位置 $mx$,那么凡是左端点 $\in [l,mx]$、右端点 $\in (mx,r]$ 的子区间,最大值都是 $a_{mx}$。因此问题转化成 $[l,mx)$、$(mx,r]$ 的子问题。
  考虑 $(mx,r]$,重要的性质是,$mx$ 一定在 $r$ 的转移路径上,因此 $f_r-f_{mx}$ 就是左端点 $\in (mx,r]$、右端点为 $r$ 的子区间的 $\max$ 和;同理,$mx$ 一定也在 $r-1$ 的转移路径上,所以 $f_{r-1}-f_{mx}$ 就是左端点 $\in (mx,r-1]$、右端点为 $r-1$ 的子区间的 $\max$ 和……
  因此 $(mx,r]$ 的贡献就是

  所以求个 $f$ 的前缀和就做好了。
  同理可求 $[l,mx)$ 的贡献,以及 $\min$ 和。

  时间复杂度,预处理 rmq 需要 $O(n \log n)$,预处理 $f$ 需要 $O(n)$,每个询问是 $O(1)$ 的,因此是 $O(n \log n + m)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#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);
}
}
}