3085 吃遍赴丝码(分治)
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∑n?j=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∑p1??j?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]即可。
綜上,我們可以在nlog?nn \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; }總結
以上是生活随笔為你收集整理的3085 吃遍赴丝码(分治)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU6956-Pass!(2021杭电
- 下一篇: 进阶面试的必看的ORM架构之 ORM简介