普通版:题目链接
数据加强版:题目链接
数据加强版 加强版:题目链接
题目大意
给定一个数列,每次给出一个区间,求区间中所有子段的最小值之和。
n
,
m
≤
1
0
5
n,m\le 10^5
n,m≤105
题解
显然是单调栈先跑一遍,然后接下来有若干个做法:
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(p−l+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+1∑rs[i]−s[p](r−p)+i=l∑p−1s′[i]−s′[p](p−l)+a[p](r−p+1)(p−l+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;
}