@ZCDHJ
2019-07-30T08:44:43.000000Z
字数 3110
阅读 577
未分类
使用莫队解决本题。考虑计算以每个数为终点的所有子串的最小值之和。 这里 代表前面第一个比 小的数字的位置。这个可以用 ST 表加二分做到 预处理。然后莫队转移区间时怎么计算右边新加入的数字造成的贡献?设当前q区间中最小数的位置为 ,权值为 。那么所有右端点为新加入的点,左端点在 左边(包括 )的子区间的z最小值都是 ,这部分区间的答案就是 。对于左端点在 右边的子区间, 即为答案。因为 中所有左端点超过 的也就是不需要的子区间的答案其实就是 ,画个图就很好理解。对于其他情况如删除也同理。注意在莫队转移的时候要先添加点再删除。
#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>typedef long long LL;#define int LLconst int MAXN = 1e5;int n, m, blockSize, allL, allR, allAns;int a[MAXN | 1], pre[MAXN | 1], nxt[MAXN | 1], rmqTable[18][MAXN | 1], rmqTablePos[18][MAXN | 1], lg[MAXN | 1];int dp[2][MAXN | 1], ans[MAXN | 1];struct Query {int l, r, belong, id;Query() : l(0), r(0), belong(0), id(0) {}friend bool operator< (const Query &lhs, const Query &rhs) {return (lhs.belong < rhs.belong) || (lhs.belong == rhs.belong && lhs.r < rhs.r);}} que[MAXN | 1];inline int read() {register int x = 0, v = 1;register char ch = getchar();while (!isdigit(ch)) {if (ch == '-') v = -1;ch = getchar();}while (isdigit(ch)) {x = x * 10 + ch - '0';ch = getchar();}return x * v;}inline int calcMin(int l, int r) {return std::min(rmqTable[lg[r - l + 1]][l], rmqTable[lg[r - l + 1]][r - (1 << lg[r - l + 1]) + 1]);}inline int calcMinPos(int l, int r) {return rmqTable[lg[r - l + 1]][l] < rmqTable[lg[r - l + 1]][r - (1 << lg[r - l + 1]) + 1] ? rmqTablePos[lg[r - l + 1]][l] : rmqTablePos[lg[r - l + 1]][r - (1 << lg[r - l + 1]) + 1];}void getPreAndNxt() {for (int i = 1; i <= n; ++i) {int l = 1, r = i, mid;pre[i] = 0;while (l <= r) {mid = (l + r) >> 1;if (calcMin(mid, i) < a[i]) {pre[i] = mid;l = mid + 1;} else r = mid - 1;}}for (int i = n; i >= 1; --i) {int l = i, r = n, mid;nxt[i] = n + 1;while (l <= r) {mid = (l + r) >> 1;if (calcMin(i, mid) < a[i]) {nxt[i] = mid;r = mid - 1;} else l = mid + 1;}}for (int i = n; i >= 1; --i) dp[0][i] = dp[0][nxt[i]] + (nxt[i] - i) * a[i];for (int i = 1; i <= n; ++i) dp[1][i] = dp[1][pre[i]] + (i - pre[i]) * a[i];}void add(int x, int opt) {if (opt == 0) {int minVal = calcMin(allL, allR), minPos = calcMinPos(allL, allR);allAns += dp[0][x] - dp[0][minPos] + minVal * (allR - minPos + 1);} else {int minVal = calcMin(allL, allR), minPos = calcMinPos(allL, allR);allAns += dp[1][x] - dp[1][minPos] + minVal * (minPos - allL + 1);}}void del(int x, int opt) {if (opt == 0) {int minVal = calcMin(allL, allR), minPos = calcMinPos(allL, allR);allAns -= dp[0][x] - dp[0][minPos] + minVal * (allR - minPos + 1);} else {int minVal = calcMin(allL, allR), minPos = calcMinPos(allL, allR);allAns -= dp[1][x] - dp[1][minPos] + minVal * (minPos - allL + 1);}}signed main() {n = read();m = read();blockSize = sqrt(n);memset(rmqTable, 0x3f, sizeof(rmqTable));for (int i = 1; i <= n; ++i) a[i] = read(), rmqTable[0][i] = a[i], rmqTablePos[0][i] = i;for (int i = 1; i < 18; ++i) {for (int j = 1; j + (1 << i) - 1 <= n; ++j) {if (rmqTable[i - 1][j] < rmqTable[i - 1][j + (1 << (i - 1))]) {rmqTable[i][j] = rmqTable[i - 1][j];rmqTablePos[i][j] = rmqTablePos[i - 1][j];} else {rmqTable[i][j] = rmqTable[i - 1][j + (1 << (i - 1))];rmqTablePos[i][j] = rmqTablePos[i - 1][j + (1 << (i - 1))];}}}lg[1] = 0;for (int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;getPreAndNxt();for (int i = 1; i <= m; ++i) {que[i].l = read();que[i].r = read();que[i].id = i;que[i].belong = (que[i].l - 1) / blockSize + 1;}std::sort(que + 1, que + 1 + m);int &l = allL, &r = allR;l = 1;r = 1;allAns = a[1];for (int i = 1; i <= m; ++i) {while (l > que[i].l) add(--l, 0);while (r < que[i].r) add(++r, 1);while (l < que[i].l) del(l, 0), ++l;while (r > que[i].r) del(r, 1), --r;ans[que[i].id] = allAns;}for (int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);return 0;}