码迷,mamicode.com
首页 > Windows程序 > 详细

AcWing 334. K匿名序列

时间:2020-02-28 22:58:41      阅读:80      评论:0      收藏:0      [点我收藏+]

标签:return   problem   iostream   stream   line   turn   匿名   最小   ons   

大型补档计划

题目链接

就是把序列分成无数段,每段长度 $ >= K$,然后 \([l, r]\) 这段的花费是 \(S[r] - S[l - 1] - (r - l + 1) * a[l]\) (把所有数减成 \(a[l]\)

很容易列出状态转移方程:
\(f[i]\) 为前 i 个分完段的最小花费
\(f[i] = f[j] + s[i] - s[j] - (i - j) * a[j + 1]\)
移项:
\(\underline{f[j] - s[j] + j * a[j + 1]}_y = \underline{i}_k * \underline{a[j + 1]}_x - \underline{s[i] + f[i]}_b\)

一个鲜明的斜率优化,其中斜率、横坐标都是递增的,用弹出法即可。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 500005;
const LL INF = 0x3f3f3f3f3f3f3f3f;

int n, K, a[N], q[N];
LL f[N], s[N];
LL inline y(int i) { return f[i] - s[i] + i * (LL)a[i + 1]; } 
LL inline x(int i) { return a[i + 1]; }
int main() {
    int T; scanf("%d", &T);
    while (T--) {
        memset(f, 0x3f, sizeof f);
        scanf("%d%d", &n, &K);
        for (int i = 1; i <= n; i++) 
            scanf("%d", a + i), s[i] = s[i - 1] + a[i];
        f[0] = 0;
        int hh = 0, tt = 0;
        for (int i = K; i <= n; i++) {
            if (i - K >= K) {
                while (hh < tt && (y(q[tt]) - y(q[tt - 1])) * (x(i - K) - x(q[tt])) >= (y(i - K) - y(q[tt])) * (x(q[tt]) - x(q[tt - 1]))) tt--;
                q[++tt] = i - K;
            }  
            while (hh < tt && y(q[hh + 1]) - y(q[hh]) <= i * (x(q[hh + 1]) - x(q[hh]))) hh++;
            if (hh <= tt) f[i] = f[q[hh]] + s[i] - s[q[hh]] - (LL)(i - q[hh]) * a[q[hh] + 1];
        }
        printf("%lld\n", f[n]);
    }
    return 0;
}

AcWing 334. K匿名序列

标签:return   problem   iostream   stream   line   turn   匿名   最小   ons   

原文地址:https://www.cnblogs.com/dmoransky/p/12380602.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!