题目大意 定义一个序列的 average 为 最大值 + 最小值 2 \frac{最大值+最小值}{2} 2 最大值 + 最小值 。 给定一个序列 a 1 , ⋯ , a n a_1,\cdots,a_n a 1 , ⋯ , a n ,有 m m m 次询问,每次问这个区间的所有子区间的 average 期望。
n , m ≤ 2 × 10 5 , 1 ≤ a i ≤ 10 9 n,m \le 2 \times 10^5,\ 1 \le a_i \le 10^9 n , m ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 1 0 9 多测,∑ n , ∑ m ≤ 3 × 10 5 \sum n,\sum m \le 3 \times 10^5 ∑ n , ∑ m ≤ 3 × 1 0 5 8s
扯淡 考场上脑补了个 O ( m n ) O(m \sqrt n) O ( m n ) 的只增莫队巨难写,写到最后又 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 [ 2 m a x + m i n ] = 2 E [ m a x ] + E [ m i n ] ,所以问题变成每次求一个区间所有子区间的 max \max max 和以及 min \min min 和。这就是【HNOI2016 序列】了。
做法多种多样,莫队和在线 O ( n log n + m ) O(n \log n+m) O ( n log n + m ) 的都有,但其实本质相同的,莫队到在线也就多一步小转化而已。下面就讲在线的。 考虑这样一个数组:f i f_i f i 表示右端点为 i i i 、左端点 ∈ [ 1 , i ] \in [1,i] ∈ [ 1 , i ] 的所有区间的 max \max max 和。它的转移很简单,就是找到 i i i 上一个比它大的数 L i L_i L i ,那么左端点 ≤ L i \le L_i ≤ L i 的区间的 max \max max 都保持不变,左端点 ∈ ( L i , i ] \in (L_i,i] ∈ ( L i , i ] 的区间的 max \max max 等于 a i a_i a i ,因此
f i = f L i + a i ( i − L i ) f_i=f_{L_i}+a_i(i-L_i) f i = f L i + a i ( i − L i )
这东西怎么用呢?变形得到 f i − f L i = a i ( i − L i ) f_i-f_{L_i}=a_i(i-L_i) f i − f L i = a i ( i − L i ) ,也就是说,知道了 i i i 的转移点 L i L_i L i ,那么就知道了左端点 ∈ ( L i , i ] \in(L_i,i] ∈ ( L i , i ] 、右端点为 i i i 的所有区间的 max \max max 和。 更进一步,i i i 从 L i L_i L i 转移来,L i L_i L i 从 L L i L_{L_i} L L i 转移来……这样形成一个转移路径(实际上就是以 i i i 结尾的单调栈),在这条路径上的任何一个 j j j ,都满足 f i − f j f_i-f_j f i − f j 等于左端点 ∈ ( 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 m x ,那么凡是左端点 ∈ [ l , m x ] \in [l,mx] ∈ [ l , m x ] 、右端点 ∈ ( m x , r ] \in (mx,r] ∈ ( m x , r ] 的子区间,最大值都是 a m x a_{mx} a m x 。因此问题转化成 [ l , m x ) [l,mx) [ l , m x ) 、( m x , r ] (mx,r] ( m x , r ] 的子问题。 考虑 ( m x , r ] (mx,r] ( m x , r ] ,重要的性质是,m x mx m x 一定在 r r r 的转移路径上,因此 f r − f m x f_r-f_{mx} f r − f m x 就是左端点 ∈ ( m x , r ] \in (mx,r] ∈ ( m x , r ] 、右端点为 r r r 的子区间的 max \max max 和;同理,m x mx m x 一定也在 r − 1 r-1 r − 1 的转移路径上,所以 f r − 1 − f m x f_{r-1}-f_{mx} f r − 1 − f m x 就是左端点 ∈ ( m x , r − 1 ] \in (mx,r-1] ∈ ( m x , r − 1 ] 、右端点为 r − 1 r-1 r − 1 的子区间的 max \max max 和…… 因此 ( m x , r ] (mx,r] ( m x , 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) ( 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 f f 的前缀和就做好了。 同理可求 [ l , m x ) [l,mx) [ l , m x ) 的贡献,以及 min \min min 和。
时间复杂度,预处理 rmq 需要 O ( n log n ) O(n \log n) O ( n log n ) ,预处理 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 ( 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); } } }