2021HDU多校第四场5-Didn‘t I Say to Make My Abilities Average in the Next Life?!

本文探讨了一种使用线段树和单调栈解决HDU6989问题的方法,针对给定序列的区间平均值查询,通过离线处理和维护历史信息,实现了期望值的高效计算。文章详细介绍了如何利用线段树维护A矩阵,并利用单调栈追踪最大值和最小值区间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接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 1r 到它的最值(下面以最大值为例),用 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+1i)A[i][j],被替换的第 k k k行减去 ( n − k + 1 ) ∗ a [ k − 1 ] [ j ] (n-k+1)*a[k-1][j] (nk+1)a[k1][j] ,然后两者作差一下就是在第 i 行减去 i ∗ A [ i ] [ j ] i*A[i][j] iA[i][j] ,第 k 行加上 k ∗ A [ i ] [ j ] k *A[i][j] kA[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值