题目链接HDU 6989
题意:
有一个序列 {
a
n
a_n
an} 给定m个询问,每次给定
l
,
r
l,r
l,r ,随机在
[
l
,
r
]
[l,r]
[l,r] 中选出一个子区间,计算平均值。这里平均值的定义为
(
m
a
x
{
a
l
,
.
.
.
,
a
r
}
+
m
i
n
{
a
l
,
.
.
.
,
a
r
}
)
/
2
(max\{a_l, ... , a_r\} + min \{a_l, ..., a_r \}) / 2
(max{al,...,ar}+min{al,...,ar})/2,求
[
l
,
r
]
[l,r]
[l,r]平均值的期望。
思考:
首先该问题的平均值可以分为最大值和最小值来分别求。
这道题如果只是单纯的考虑
[
1
,
n
]
[1,n]
[1,n],那么就是一个经典的单调栈维护每个
a
i
a_i
ai 的为最值的区间。
但是考虑对于有多个询问,我们考虑如何来实现。
我们离线该问题,然后依旧采用枚举右端点,然后去查询左端点的方式来解决问题。
如果对于每个右端点
r
r
r,我们都处理左端点
1
−
r
1-r
1−r 到它的最值(下面以最大值为例),用
A
[
R
,
i
]
A[R, i]
A[R,i] 来表示该值。
对于 A 矩阵,我们可以知道,每次查询的的就是从
A
[
L
,
L
]
A[L,L]
A[L,L] 到
A
[
R
,
R
]
A[R,R]
A[R,R] 的子矩阵。
考虑如何去维护这个矩阵?
这里引入一个经典问题—线段树维护历史信息和
我们对于第 i 行,知道了
A
[
i
]
[
1
]
.
.
.
A
[
i
]
[
i
]
A[i][1] ... A[i][i]
A[i][1]...A[i][i] ,那么对于
A
[
i
+
1
]
A[i + 1]
A[i+1] 的这一行,只会改变
a
[
i
+
1
]
a[i +1]
a[i+1] 为最值的那个区间,即从上一个比
a
[
i
+
1
]
a[i+1]
a[i+1] 大的那个元素后一个开始,到
i
+
1
i + 1
i+1 ,这一段区间都是的值都是
a
[
i
+
1
]
a[i +1]
a[i+1]。这样对每个
A
[
i
]
A[i]
A[i] 可以用一棵线段树来维护,而上一个大于它的可以用单调栈来维护。
由于这道题可以离线,我们可以用一棵线段树,采用树状数组类似的方式来维护每一个 A [ ] [ j ] A[][j] A[][j],在 A [ i ] [ j ] A[i][j] A[i][j] 第一次出现的时候,给后面每一行这个位置都加上 A [ i ] [ j ] A[i][j] A[i][j] ,然后在被替换的时候,后面每一行都减去 A [ i ] [ j ] A[i][j] A[i][j] ,这样的话可以采用在第一次出现的第 i i i 行加上 ( n + 1 − i ) ∗ A [ i ] [ j ] (n+1-i) * A[i][j] (n+1−i)∗A[i][j],被替换的第 k k k行减去 ( n − k + 1 ) ∗ a [ k − 1 ] [ j ] (n-k+1)*a[k-1][j] (n−k+1)∗a[k−1][j] ,然后两者作差一下就是在第 i 行减去 i ∗ A [ i ] [ j ] i*A[i][j] i∗A[i][j] ,第 k 行加上 k ∗ A [ i ] [ j ] k *A[i][j] k∗A[i][j],这样就的更新就是 O ( n ) O(n) O(n) 次的。
本来这道题还要维护 tag,但是可以发现加入新的值的时候,一定会先删除掉前面的点。但是覆盖的时候还是需要tag,这里可以采用一种比较巧妙的方法,直接记录这个区间对应叶子节点的值(如果都一样的话),这样的话,就可以直接通过这个记录的值来达到 tag 的效果。
对于强制在线的情况,可以采用可持久化线段树的方式,更新是 O ( n ) O(n) O(n) 级别的,所以最多新建 O ( n l o g 2 n ) O(n log_2n) O(nlog2n) 个点。复杂度是可以接受的。
#include<bits/stdc++.h>
#define For(aa, bb, cc) for(int aa = (bb); aa <= (int)(cc); ++aa)
#define mk make_pair
using namespace std;
using Pair = pair<int, int>;
typedef long long LL;
const int maxn = 2e5 + 10;
constexpr int mod = 1e9 + 7;
int n, m;
int a[maxn];
vector<Pair> G[maxn];
int stmax[maxn], topa, stmin[maxn], topi;
LL ans[maxn];
LL qpow(LL x, LL y = mod - 2){
LL res = 1;
while(y){
if(y & 1) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
void fadd(LL &x, LL y){
x += y;
if(x >= mod) x -= mod;
}
#define lr (node << 1)
#define rr (node << 1 | 1)
#define mid ((l + r) >> 1)
LL sum[maxn << 3], tr[maxn << 3];
LL point[maxn << 3], pointid[maxn << 3];
//sum: history sum, tr: now val; only cover the interval
void update(int node, int l, int r, int L, int R, int val, int id){
fadd(sum[node], mod - 1ll * (R - L + 1) * val % mod * id % mod);
fadd(tr[node], 1ll * (R - L + 1) * val % mod);
//cout<<node<<" "<<l<<" "<<r<<" "<<val<<" "<<id<<" ";
//cout<<sum[node]<<" "<<tr[node]<<endl;
if(L <= l && r <= R){
fadd(point[node], val);
fadd(pointid[node], mod - 1ll * val * id % mod);
//cout<<l<<" "<<r<<" "<<point[node]<<" "<<pointid[node]<<endl;
return ;
}
if(R <= mid) return update(lr, l, mid, L, R, val, id);
else if(L > mid) return update(rr, mid + 1, r, L, R, val, id);
update(lr, l, mid, L, mid, val, id);
update(rr, mid + 1, r, mid + 1, R, val, id);
}
LL query(int node, int l, int r, int L, int R, int id){
if(L <= l && r <= R) return (1ll * id * tr[node] + sum[node]) % mod;
LL ans = 1ll * (point[node] * id % mod + pointid[node]) * (R - L + 1) % mod;
if(R <= mid) ans += query(lr, l, mid, L, R, id);
else if(L > mid) ans += query(rr, mid + 1, r, L, R, id);
else return ans += query(lr, l, mid, L, mid, id) + query(rr, mid + 1, r, mid + 1, R, id);
return ans % mod;
}
#undef lr
#undef rr
#undef mid
void solve(){
topa = topi = 0;
For(i, 1, n){
while(topa && a[stmax[topa]] <= a[i]){
update(1, 1, n, stmax[topa - 1] + 1, stmax[topa], mod - a[stmax[topa]], i);
--topa;
}
while(topi && a[stmin[topi]] >= a[i]){
update(1, 1, n, stmin[topi - 1] + 1, stmin[topi], mod - a[stmin[topi]], i);
--topi;
}
/*
printf("stmax: ");
For(j,1,topa) printf("%d ", stmax[j]);
puts("");
printf("stmin: ");
For(j,1,topi) printf("%d ", stmin[j]);
puts("");
*/
update(1, 1, n, stmax[topa] + 1, i, a[i], i);
stmax[++topa] = i;
update(1, 1, n, stmin[topi] + 1, i, a[i], i);
stmin[++topi] = i;
for(auto u: G[i]){
int l = u.first, r = i, id = u.second;
ans[id] = query(1, 1, n, l, r, i + 1) % mod;
//printf("ans: %lld\n",ans[id]);
ans[id] = ((mod + 1) >> 1ll) * ans[id] % mod * qpow(1ll * (r - l + 1) * (r - l + 2) / 2 % mod) % mod;
}
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int _;
scanf("%d", &_);
while(_--){
scanf("%d%d", &n ,&m);
For(i, 1, n) scanf("%d", &a[i]);
For(i, 1, n) G[i].clear();
For(i, 1, n << 2) sum[i] = tr[i] = point[i] = pointid[i] = 0;
For(i, 1, m){
int x, y;
scanf("%d%d", &x, &y);
G[y].push_back(mk(x, i));
}
solve();
For(i, 1, m) printf("%lld\n", ans[i]);
}
return 0;
}