题目链接:http://gdutcode.sinaapp.com/problem.php?cid=1031&pid=4
这个题目自然会考虑到去讨论最长或者最短的板子。
笔上大概模拟一下的话,就会知道,假设最长的板子是r,0和n+1位置上都是高度为0的板子,那么对于[0, r-1]中的最长板子rr,rr到r这一短应该都是被深度为a[rr]的水覆盖。同样的[0, rr-1]中的最长板子rrr,rrr到rr这一段应该是被a[rrr]覆盖,以此类推可以搞定r的前面一段,同理搞定后一段。
关于最值这一块,可以使用RMQ之类的logn维护,总的复杂度是nlogn。但是考虑到区间都是[0, r]和[r, n+1]这种的。所以很容易想到DP,p[0][i]表示从0到i区间内最大值的角标,p[1][i]表示从i到n+1区间内最大值的角标。然后两遍方程转移。总的复杂度是O(n)。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <set> #include <map> #include <queue> #include <vector> #include <string> #define LL long long using namespace std; const int maxN = 1e6+5; int n, a[maxN]; //RMQ-ST算法 //效率nlogn //查询区间最值,注意区间[0, n-1]和[1, n]的区别 int ma[maxN][20]; void RMQ() { memset(ma, 0, sizeof(ma)); for (int i = 0; i <= n+1; ++i) ma[i][0] = i; for (int j = 1; (1<<j) <= n+2; ++j) for (int i = 0; i+(1<<j)-1 <= n+1; ++i) { if (a[ma[i][j-1]] > a[ma[i+(1<<(j-1))][j-1]]) ma[i][j] = ma[i][j-1]; else ma[i][j] = ma[i+(1<<(j-1))][j-1]; } } int query(int lt, int rt) { int k = 0; while ((1<<(k+1)) <= rt-lt+1) k++; if (a[ma[lt][k]] > a[ma[rt-(1<<k)+1][k]]) return ma[lt][k]; else return ma[rt-(1<<k)+1][k]; } void input() { scanf("%d", &n); a[0] = a[n+1] = 0; for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); RMQ(); } LL cal(int lt, int rt, int h) { LL ans = 0; for (int i = lt; i <= rt; ++i) ans += max(0, h-a[i]); return ans; } void work() { LL ans = 0; int mid = query(0, n+1), k, now; now = mid; while (now > 0) { k = query(0, now-1); ans += cal(k, now-1, a[k]); now = k; } now = mid; while (now < n+1) { k = query(now+1, n+1); ans += cal(now+1, k, a[k]); now = k; } cout << ans << endl; } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 1; times <= T; ++times) { input(); work(); } return 0; }
时间: 2024-11-19 01:09:30