ACM学习历程—广东工业大学2016校赛决赛-网络赛E 积木积水(最值问题 || 动态规划)

题目链接:http://gdutcode.sinaapp.com/problem.php?cid=1031&pid=4

这个题目自然会考虑到去讨论最长或者最短的板子。

笔上大概模拟一下的话,就会知道,假设最长的板子是r0n+1位置上都是高度为0的板子,那么对于[0, r-1]中的最长板子rrrrr这一短应该都是被深度为a[rr]的水覆盖。同样的[0, rr-1]中的最长板子rrrrrrrr这一段应该是被a[rrr]覆盖,以此类推可以搞定r的前面一段,同理搞定后一段。

关于最值这一块,可以使用RMQ之类的logn维护,总的复杂度是nlogn。但是考虑到区间都是[0, r][r, n+1]这种的。所以很容易想到DPp[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;
}
View Code
原文地址:https://www.cnblogs.com/andyqsmart/p/5375240.html