【DP】CCC ’19 S4 – Tourism

传送门


给定N个数,代表N个景点的权值,必须顺序参观完。每天最多参观K个景点,但是必须严格N/K+(N%K!=0)天参观完。当天的收获为当天景点的最大权值,问总收获最大是多少。
N和K都小于1E6


先考虑这样一种状态表示:
如果把每K个景点分成一份,最后一份可能会少K-N%K个。记为M。
假设每天的目标是一份。
f[i][j] 表示前 i 天偷了 j 天懒。也就是说本来应该走i * k 个景点,但是只走了i * k – j个。
很明显只要确定了偷懒天数,第x天停留的位置就固定了。所以第二维也可以换为停留的位置。
假设停留在下图的i点。
很明显f[x][i]=\max\limits_{j=i-k \to st} (f[x-1][j]+\max\limits_{k=j\to i}a[k])


由于每个阶段都是固定长度,所以很好判断当i%k==0的时候肯定是一个阶段的结束。
这样可以把dp状态优化到一维。

然后由于dp[j]+maxA[j+1,st]这一部分是不变的,可以在每个阶段结束的时候预处理出来。
用树状数组维护这个值的后缀区间最大。同时mxdp[x]记录dp[x,st]最大值和mxA[x]记录A[x+1,st]的最大值。下一个阶段可以直接使用。

当下一个阶段开始的时候,让a[i]mxA[j]进行比较,如果a[i]更大,那么更新[j,i]的树状数组。同时j往后退。

当i向前移动一步的时候,同样也是更新[j,i]的树状数组。

次数树状数组[j,i]的最大值就是dp[i]的值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 233;
ll c[maxn], dp[maxn], mxdp[maxn];
void update(int i, ll val)
{
    for(int x = maxn - i - 1; x < maxn; x += x & -x) c[x] = max(c[x], val); 
}
ll suffixAsk(int i)
{
    ll ret = 0;
    for(int x = maxn - i - 1; x; x -= x & -x) ret = max(ret, c[x]);
    return ret;
}
int day[maxn], a[maxn];
int ma[maxn]; // max suffix a[] value of prevoius day
int n, k;
int main()
{
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = n%k, j = 1; i <= n; i += k) day[j++] = i;
    for(int i = 1, today = 1, back = 0, _maxa = 0; i <= n; i++)
    {
        int lim = day[today - 1];
        _maxa = max(a[i], _maxa);
        while(ma[back] <= _maxa && max(lim, i - k) <= back)
        {
            ma[back] = a[i];
            update(back, mxdp[back] + a[i]);
            if(ma[back - 1] <= _maxa && max(lim, i - k) <= back - 1) back--;
            else break;
        }
        if(max(lim, i - k) > back)
        {
            update(back + 1, mxdp[back + 1] + (ma[back + 1] = _maxa));
            back++;
        }
        dp[i] = suffixAsk(max(lim, i - k));
        if(i % k == 0)
        {
            int max2 = 0;
            for(int j = i; j >= max(day[today], i - k); j--)
            {
                //ma[j] = max(ma[j + 1], a[j]);
                //update(j, dp[j] + ma[j + 1]);
                update(j, dp[j] + (ma[j] = max2));
                max2 = max(max2, a[j]);
                mxdp[j] = max(mxdp[j + 1], dp[j]);
            }
            today++;
            back = i;
            _maxa = 0;
        }
    }
    cout << dp[n] << endl;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注