3085 吃遍赴丝码
给定一个长度为nnn的数组,求∑i=1n∑j=in(j−i+1)×min{ai,…,aj}×max{ai,…,aj}\sum\limits_{i = 1} ^{n} \sum\limits_{j = i} ^{n} (j - i + 1) \times min\{a_i, \dots, a_j\} \times max\{a_i, \dots, a_j\}i=1∑nj=i∑n(j−i+1)×min{ai,…,aj}×max{ai,…,aj}。
考虑分治求解,f(l,mid),f(mid+1,r)f(l, mid), f(mid + 1, r)f(l,mid),f(mid+1,r),返回的是区间[l,mid],[mid+1,r][l, mid], [mid + 1, r][l,mid],[mid+1,r]的值,考虑两个区间合并的时候如何计算。
我们考虑枚举左端点i∈[l,mid]i \in [l, mid]i∈[l,mid],j∈[mid+1,r]j \in [mid + 1, r]j∈[mid+1,r],设maxn=max{ai,…amid},minn=min{ai,…,amid}maxn = max\{a_i, \dots a_{mid}\}, minn = min\{a_i, \dots, a_{mid}\}maxn=max{ai,…amid},minn=min{ai,…,amid},
我们在区间[mid+1,r][mid + 1, r][mid+1,r]上找到,最靠右的p1p_1p1,满足min{amid+1,…,ap1}≥minnmin\{a_{mid + 1}, \dots, a_{p_1}\} \ge minnmin{amid+1,…,ap1}≥minn,
同样在区间[mid+1,r][mid + 1, r][mid+1,r]上找打,最靠右的p2p_2p2,满足max{amid+1,…,ap2}≤maxnmax\{a_{mid + 1}, \dots, a_{p_2} \} \le maxnmax{amid+1,…,ap2}≤maxn,
我们假设p1≤p2p_1 \le p_2p1≤p2,则我们可以把区间[mid+1,r][mid + 1, r][mid+1,r]分成三个部分[mid+1,p1],[p1+1,p2],[p2+1,r][mid + 1, p_1], [p_1 + 1, p_2], [p_2 + 1, r][mid+1,p1],[p1+1,p2],[p2+1,r]。
则在第一段j∈[mid+1,p1]j \in [mid + 1, p_1]j∈[mid+1,p1]上显然有min{ai,…,aj}=minn,max{ai,…,aj}=maxnmin\{a_i, \dots, a_j\} = minn, max\{a_i, \dots, a_j\} = maxnmin{ai,…,aj}=minn,max{ai,…,aj}=maxn,
即答案为minn×maxn×∑j=mid+1p1j−i+1minn \times maxn \times \sum\limits_{j = mid + 1} ^{p_1}j - i + 1minn×maxn×j=mid+1∑p1j−i+1。
则在第二段j∈[p1+1,p2]j \in[p_1 + 1, p_2]j∈[p1+1,p2]上显然有max{ai,…,aj}=maxnmax\{a_i, \dots, a_j\} = maxnmax{ai,…,aj}=maxn,区间minminmin则是[mid+1,j][mid + 1, j][mid+1,j]的最小值了,可以考虑记录一个前缀minminmin,
即答案为maxn×∑j=p1+1p2(j−i+1)×min[j]maxn \times \sum\limits_{j = p_1 + 1} ^{p_2} (j - i + 1) \times min[j]maxn×j=p1+1∑p2(j−i+1)×min[j],这个时候只要预处理出前缀∑min[j]×j,∑min[j]\sum min[j] \times j, \sum min[j]∑min[j]×j,∑min[j]即可。
如果不满足p1≤p2p_1 \le p_2p1≤p2,我们可以同理处理∑max[j]×j,∑max[j]\sum max[j] \times j, \sum max[j]∑max[j]×j,∑max[j]即可。
则在第三段j∈[p2+1,r]j \in [p_2 + 1, r]j∈[p2+1,r]上显然有min,maxmin, maxmin,max都在[mid+r,j][mid + r, j][mid+r,j]上,所以考虑记录一个前缀min,maxmin, maxmin,max,
即答案为∑j=p2+1r(j−i+1)×min[j]×max[j]\sum\limits_{j = p_2 + 1} ^{r} (j - i + 1) \times min[j] \times max[j]j=p2+1∑r(j−i+1)×min[j]×max[j],预处理出∑min[j]×max[j]×j,∑min[j]×max[j]\sum min[j] \times max[j] \times j, \sum min[j] \times max[j]∑min[j]×max[j]×j,∑min[j]×max[j]即可。
综上,我们可以在nlognn \log nnlogn的时间内得到答案。
#include <bits/stdc++.h>using namespace std;const int N = 5e5 + 10, mod = 1000000000;int a[N], lmin[N], lmax[N], rmin[N], rmax[N], n;int sum1[N], sum2[N], sum3[N], sum4[N], sum5[N], sum6[N];// min * j, min, max * j, max, min * max * j, min * maxinline int add(int x, int y) {return x + y < mod ? x + y : x + y - mod;
}inline int sub(int x, int y) {return x >= y ? x - y : x - y + mod;
}inline int calc(int i, int l, int r) {int fi = l - i + 1, se = r - i + 1, num = r - l + 1;return 1ll * (fi + se) * num / 2 % mod;
}int f(int l, int r) {if (l == r) {return 1ll * a[l] * a[l] % mod;}int mid = l + r >> 1, ans = add(f(l, mid), f(mid + 1, r));// f(l, mid), f(mid + 1, r);// 预处理 [i, mid] 的最小最大值。lmin[mid] = lmax[mid] = a[mid];for (int i = mid - 1; i >= l; i--) {lmin[i] = min(lmin[i + 1], a[i]);lmax[i] = max(lmax[i + 1], a[i]);}// 预处理 [mid + 1, j]的最小最大值。rmin[mid + 1] = rmax[mid + 1] = a[mid + 1];for (int i = mid + 2; i <= r; i++) {rmin[i] = min(rmin[i - 1], a[i]);rmax[i] = max(rmax[i - 1], a[i]);}sum1[mid] = sum2[mid] = sum3[mid] = sum4[mid] = sum5[mid] = sum6[mid] = 0;for (int i = mid + 1; i <= r; i++) {sum1[i] = add(sum1[i - 1], 1ll * i * rmin[i] % mod);sum2[i] = add(sum2[i - 1], rmin[i]);sum3[i] = add(sum3[i - 1], 1ll * i * rmax[i] % mod);sum4[i] = add(sum4[i - 1], rmax[i]);sum5[i] = add(sum5[i - 1], 1ll * i * rmin[i] % mod * rmax[i] % mod);sum6[i] = add(sum6[i - 1], 1ll * rmin[i] * rmax[i] % mod);}int p1 = mid, p2 = mid; // min, max;for (int i = mid; i >= l; i--) {while (p1 < r && rmin[p1 + 1] >= lmin[i]) {p1++;}while (p2 < r && rmax[p2 + 1] <= lmax[i]) {p2++;}// [mid + 1, min(p1, p2)]ans = add(ans, 1ll * lmin[i] * lmax[i] % mod * calc(i, mid + 1, min(p1, p2)) % mod);// [min(p1, p2) + 1, max(p1, p2)]if (p1 < p2) {int l = p1 + 1, r = p2;ans = add(ans, 1ll * lmax[i] * sub(sum1[r], sum1[l - 1]) % mod);ans = sub(ans, 1ll * lmax[i] * (i - 1) % mod * sub(sum2[r], sum2[l - 1]) % mod);}else if (p2 < p1) {int l = p2 + 1, r = p1;ans = add(ans, 1ll * lmin[i] * sub(sum3[r], sum3[l - 1]) % mod);ans = sub(ans, 1ll * lmin[i] * (i - 1) % mod * sub(sum4[r], sum4[l - 1]) % mod);}// [max(p1, p2) + 1, r]int l = max(p1, p2) + 1;ans = add(ans, sub(sum5[r], sum5[l - 1]));ans = sub(ans, 1ll * (i - 1) * sub(sum6[r], sum6[l - 1]) % mod);}return ans;
}int main() {freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);}printf("%d\n", f(1, n));return 0;
}