TypeDB Forces 2023 (Div. 1 + Div. 2) C (1-1)

147 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

C. Remove the Bracket

原文题面

image.png

题目描述

给定一个长度为 n(3n2e5)n(3 \le n \le 2e5) 的非负数组 ai(1in,ai2e5)a_i(1 \le i \le n,\,a_i \le 2e5) 和一个整数 s(0s2e5)s(0 \le s \le 2e5)

然后将 ai(1<i<n)a_i(1 < i < n) 分解为 xi,yix_i,\,y_i,满足xi+yi=aix_i+y_i=a_i(xis)(yis)0(x_i-s)\cdot (y_i-s) \ge 0

定义 F=a1x2+y2x3++yn2xn1+yn1anF = a_1\cdot x_2+y_2\cdot x_3+\cdots+y_{n-2}\cdot x_{n-1}+y_{n-1}\cdot a_n

要求FF值最小,输出其最小值。

题目分析

由于ai=xi+yia_i=x_i+y_i,考虑这两个数在FF中的位置

+yi1xi+yixi+1+\cdots+y_{i-1}\cdot x_i+y_i\cdot x_{i+1}+\cdots

可知当前状态与前面的状态如何分解无关,满足DP的无后效性。

接下来考虑如何使当前结果最优,即 yi1xi+yixi+1=yi1xi+(axi)xi+1y_{i-1}\cdot x_i+y_i\cdot x_{i+1} = y_{i-1}\cdot x_i+(a-x_i)\cdot x_{i+1} 最小。

可以看出其为 xix_i 的一次函数,最小值应在 xix_i 的最值处取到。

由于 xi+yi=aix_i+y_i=a_i(xis)(yis)0(x_i-s)\cdot (y_i-s) \ge 0, 当aisa_i\le sxix_i 可以取 00ssai>sa_i > s 时,xix_issaisa_i - s

定义 f[i][0/1]f[i][0/1] 表示 xix_i 分别取到两个值时最优结果。

可以看出 f[i][0/1]f[i][0/1]f[i1][[0/1]f[i-1][[0/1] 转移而来。

最初状态将 f[0][0]f[0][0]f[0][1]f[0][1] 初始化为0。

题目样例

input

10
5 0
2 0 1 3 4
5 1
5 3 4 3 5
7 2
7 6 5 4 3 2 1
5 1
1 2 3 4 5
5 2
1 2 3 4 5
4 0
0 1 1 1
5 5
4 3 5 6 4
4 1
0 2 1 0
3 99999
200000 200000 200000
6 8139
7976 129785 12984 78561 173685 15480

output

0
18
32
11
14
0
16
0
40000000000
2700826806

Accept代码(DP)

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 2e5 + 10;
int f[N][2], p[N][2];     // f[i][j]表示在数组第i个位置以当前位置第j个部分为结尾的前i个位置的F
 
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t; cin >> t;
    while (t --)
    {
        int n, s; 
        cin >> n >> s;
        for (int i = 0; i < n; i ++)
        {
            int v; cin >> v;
            if (i > 0 && i < n - 1)
            {
                if (v <= s) p[i][0] = 0, p[i][1] = v;
                else p[i][0] = s, p[i][1] = v - s;
            }
            else p[i][0] = p[i][1] = v;
        }

        f[0][0] = f[0][1] = 0;
        for (int i = 1; i < n; i ++)
        {
            f[i][0] = min(f[i - 1][0] + p[i - 1][1] * p[i][0], f[i - 1][1] + p[i - 1][0] * p[i][0]);
            f[i][1] = min(f[i - 1][0] + p[i - 1][1] * p[i][1], f[i - 1][1] + p[i - 1][0] * p[i][1]);
        }
        cout << f[n - 1][0] << "\n";
    }
    return 0;
}