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

题目大意

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

  n,m2×105, 1ai109n,m \le 2 \times 10^5,\ 1 \le a_i \le 10^9
  多测,n,m3×105\sum n,\sum m \le 3 \times 10^5
  8s

扯淡

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

题解

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

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

fi=fLi+ai(iLi)f_i=f_{L_i}+a_i(i-L_i)

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

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

(frfmx)+(fr1fmx)++(fmx+1fmx)=(i=mx+1rfi)fmx(rmx)(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)

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

  时间复杂度,预处理 rmq 需要 O(nlogn)O(n \log n),预处理 ff 需要 O(n)O(n),每个询问是 O(1)O(1) 的,因此是 O(nlogn+m)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);
}
}
}