HNOI2016序列+数据加强版(前缀和+单调栈)

普通版:题目链接
数据加强版:题目链接
数据加强版 加强版:题目链接

题目大意

给定一个数列,每次给出一个区间,求区间中所有子段的最小值之和。
n , m ≤ 1 0 5 n,m\le 10^5 n,m105

题解

显然是单调栈先跑一遍,然后接下来有若干个做法:

1.莫队

这题莫队解法也很神啊qwq
考虑加入点时对答案的贡献,以右端点为例。令 p p p表示当前询问区间 [ l , r ] [l,r] [l,r]中的最小值,那么 r r r的贡献实际上就是 a p ( p − l + 1 ) + s [ r ] − s [ p ] a_p(p-l+1)+s[r]-s[p] ap(pl+1)+s[r]s[p],其中 s [ r ] s[r] s[r]表示区间 [ 1 , r ] [1,r] [1,r] r r r为右端点的贡献,这个在单调栈的时候就可以dp出来。
于是用st表查询最小值就可以在 O ( n n ) O(n\sqrt n) O(nn )的时间内解决了。

2.前缀和

我们考虑上面的莫队算法,如果询问区间为 [ l , r ] [l,r] [l,r],令最小值为 p p p,那么考虑如果从 p p p开始不断往左右两边加点,得到的答案为
∑ i = p + 1 r s [ i ] − s [ p ] ( r − p ) + ∑ i = l p − 1 s ′ [ i ] − s ′ [ p ] ( p − l ) + a [ p ] ( r − p + 1 ) ( p − l + 1 ) \sum_{i=p+1}^rs[i]-s[p](r-p)+\sum_{i=l}^{p-1}s'[i]-s'[p](p-l)+a[p](r-p+1)(p-l+1) i=p+1rs[i]s[p](rp)+i=lp1s[i]s[p](pl)+a[p](rp+1)(pl+1)
显然我们再记一个 s s s的前缀和就可以 O ( 1 ) O(1) O(1)回答询问了。因此总复杂度为 O ( n l o g n + m ) O(nlogn+m) O(nlogn+m),可以做完数据加强版。

3.优化最值查询

但是数据加强加强版过不了,因为瓶颈卡在了 O ( n l o g n ) O(nlogn) O(nlogn)的空间复杂度上。接下来就有了一个很神仙的ST表+分块做法。我们把原数列按照大小为 B B B分块, p r e [ i ] , s u f [ i ] pre[i],suf[i] pre[i],suf[i]分别表示从 i i i到块开头/结尾的最小值位置。
再考虑我们对于这些块建立ST表,那么如果左右端点在一个块内,我们暴力;否则我们 O ( 1 ) O(1) O(1)就可以利用上面那些东西询问最值。这个做法空间复杂度 O ( n B l o g n ) O(\frac nBlogn) O(Bnlogn),时间复杂度 O ( m B ) O(mB) O(mB),因此 B B B大概开个10左右就行了。
下面附上加强版加强版的代码。

#include <bits/stdc++.h>
namespace IOStream {
	const int MAXR = 10000000;
	char _READ_[MAXR], _PRINT_[MAXR];
	int _READ_POS_, _PRINT_POS_, _READ_LEN_;
	inline char readc() {
	#ifndef ONLINE_JUDGE
		return getchar();
	#endif
		if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
		char c = _READ_[_READ_POS_++];
		if (_READ_POS_ == MAXR) _READ_POS_ = 0;
		if (_READ_POS_ > _READ_LEN_) return 0;
		return c;
	}
	template<typename T> inline void read(T &x) {
		x = 0; register int flag = 1, c;
		while (((c = readc()) < '0' || c > '9') && c != '-');
		if (c == '-') flag = -1; else x = c - '0';
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;
	}
	template<typename T1, typename ...T2> inline void read(T1 &a, T2&... x) {
		read(a), read(x...);
	}
	inline int reads(char *s) {
		register int len = 0, c;
		while (isspace(c = readc()) || !c);
		s[len++] = c;
		while (!isspace(c = readc()) && c) s[len++] = c;
		s[len] = 0;
		return len;
	}
	inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
	inline void printc(char c) {
		_PRINT_[_PRINT_POS_++] = c;
		if (_PRINT_POS_ == MAXR) ioflush();
	}
	inline void prints(char *s) {
		for (int i = 0; s[i]; i++) printc(s[i]);
	}
	template<typename T> inline void print(T x, char c = '\n') {
		if (x < 0) printc('-'), x = -x;
		if (x) {
			static char sta[20];
			register int tp = 0;
			for (; x; x /= 10) sta[tp++] = x % 10 + '0';
			while (tp > 0) printc(sta[--tp]);
		} else printc('0');
		printc(c);
	}
	template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
		print(x, ' '), print(y...);
	}
}
using namespace IOStream;
using namespace std;
typedef long long ll;

int A, B, C, P;
ll lastans;
inline int rnd() { return A = (A * B + (C ^ (int)(lastans & 0x7FFFFFFF)) % P) % P; }
const int MAXN = 3000005, M = 8, MOD = 1000000007;
int st[20][MAXN / M + 5], sta[MAXN], n, m;
int val[MAXN], lg[MAXN], tta[MAXN];
ll pre1[MAXN], pre2[MAXN], suf1[MAXN], suf2[MAXN];
inline int get_min(int x, int y) { return val[x] < val[y] ? x : y; }
inline int query_min(int l, int r) {
	int tl = (l - 1) / M + 1, tr = (r - 1) / M + 1;
	if (tl == tr) {
		int mn = l;
		for (int i = l + 1; i <= r; i++) if (val[i] < val[mn]) mn = i;
		return mn;
	}
	if ((++tl) > (--tr)) return get_min(tta[l], sta[r]);
	int x = lg[tr - tl + 1];
	return get_min(get_min(st[x][tl], st[x][tr - (1 << x) + 1]), get_min(tta[l], sta[r]));
}
int main() {
	read(n, m);
	for (int i = 1; i <= n; i++) read(val[i]);
	read(A, B, C, P);
	int tp = 0;
	for (int i = 1; i <= n; i++) {
		for (; tp > 0 && val[sta[tp]] >= val[i]; --tp) lg[sta[tp]] = i;
		pre1[i] = pre1[sta[tp]] + (ll)val[i] * (i - sta[tp]);
		pre2[i] = pre2[i - 1] + pre1[i];
		sta[++tp] = i;
	}
	while (tp > 0) lg[sta[tp--]] = n + 1;
	for (int i = n; i > 0; i--) {
		suf1[i] = suf1[lg[i]] + (ll)val[i] * (lg[i] - i);
		suf2[i] = suf2[i + 1] + suf1[i];
	}
	int mm = (n + M - 1) / M;
	for (int i = 1; i <= n; i++) {
		if ((i - 1) % M) sta[i] = val[i] > val[sta[i - 1]] ? sta[i - 1] : i;
		else sta[i] = i;
	}
	for (int i = n; i > 0; i--) {
		if (i % M) tta[i] = val[i] > val[tta[i + 1]] ? tta[i + 1] : i;
		else tta[i] = i;
	}
	for (int i = 1; i <= mm; i++) st[0][i] = sta[min(i * M, n)];
	for (int i = 1; i < 20; i++)
	for (int j = 1; j + (1 << i) - 1 <= mm; j++)
		st[i][j] = get_min(st[i - 1][j], st[i - 1][j + (1 << i >> 1)]);
	lg[1] = 0;
	for (int i = 2; i <= mm; i++) lg[i] = lg[i >> 1] + 1;
	ll res = 0;
	while (m--) {
		int l = rnd() % n + 1, r = rnd() % n + 1;
		if (l > r) swap(l, r);
		int t = query_min(l, r);
		res += (lastans = (ll)(r - t + 1) * (t - l + 1) * val[t] + pre2[r] - pre2[t] - pre1[t] * (r - t) + suf2[l] - suf2[t] - suf1[t] * (t - l)) % MOD;
	}
	printf("%lld\n", (res % MOD + MOD) % MOD);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值