【DP】Remove the Bracket—CF1787C

Remove the Bracket—CF1787C

翻译

RSJ有一个包含 n n n个整数 a 1 , a 2 , … , a n a_1,a_2, \ldots, a_n a1,a2,,an和一个整数 s s s的序列 a a a。对于 a 2 , a 3 , … , a n − 1 a_2, a_3, \ldots, a_{n-1} a2,a3,,an1中的每个元素,他选择了一对非负整数 x i x_i xi y i y_i yi,使得 x i + y i = a i x_i+y_i=a_i xi+yi=ai ( x i − s ) ⋅ ( y i − s ) ≥ 0 (x_i-s) \cdot (y_i-s) \geq 0 (xis)(yis)0

现在他想要计算如下值:

F = a 1 ⋅ x 2 + y 2 ⋅ x 3 + y 3 ⋅ x 4 + … + y n − 2 ⋅ x n − 1 + y n − 1 ⋅ a n . F = a_1 \cdot x_2+y_2 \cdot x_3+y_3 \cdot x_4 + \ldots + y_{n - 2} \cdot x_{n-1}+y_{n-1} \cdot a_n. F=a1x2+y2x3+y3x4++yn2xn1+yn1an.

请帮助他通过选择 x i x_i xi y i y_i yi的最佳值,找到可能的最小值 F F F。可以证明至少存在一种有效的选择方法。

输入

每个测试包含多个测试用例。第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104) —— 测试用例的数量。

每个测试用例的第一行包含两个整数 n n n s s s ( 3 ≤ n ≤ 2 ⋅ 1 0 5 3 \le n \le 2 \cdot 10^5 3n2105; 0 ≤ s ≤ 2 ⋅ 1 0 5 0 \le s \le 2 \cdot 10^5 0s2105)。

第二行包含 n n n个整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an ( 0 ≤ a i ≤ 2 ⋅ 1 0 5 0 \le a_i \le 2 \cdot 10^5 0ai2105)。

保证 n n n的总和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出

对于每个测试用例,打印 F F F的可能的最小值。

注意

在第一个测试用例中, 2 ⋅ 0 + 0 ⋅ 1 + 0 ⋅ 3 + 0 ⋅ 4 = 0 2\cdot 0+0\cdot 1+0\cdot 3+0\cdot 4 = 0 20+01+03+04=0

在第二个测试用例中, 5 ⋅ 1 + 2 ⋅ 2 + 2 ⋅ 2 + 1 ⋅ 5 = 18 5\cdot 1+2\cdot 2+2\cdot 2+1\cdot 5 = 18 51+22+22+15=18

思路

易知: x i   > =   s ,   y i > = s x_i~>=~s,~y_i >= s xi >= s, yi>=s x i   < =   s ,   y i < = s x_i~<=~s,~y_i <= s xi <= s, yi<=s
由于需要得到最小的 F F F,所以 F F F 右边的乘积式中的因子的差要尽可能大
所以 a i a_i ai 分成的 x i ,   y i x_i,~y_i xi, yi 一定满足其中一个是最大值,另外一个是最小值。
即对于每个 i ∈ [ 2 , n − 1 ] i ∈[2, n - 1] i[2,n1] 进行dp( x i x_i xi y i y_i yi 中哪个取最大、哪个取最小)。

C o d e Code Code

#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 2e5 + 10;

int n, s;
int a[N];

void solve() {
	cin >> n >> s;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	
	vector <array<int, 2>> f(n + 1, {0, 0});
	// dp数组,f[i][0]:x_i取最小,y_i取最大
	vector <int> imin(n + 1, 0), imax(n + 1, 0);
	
	for (int i = 1; i <= n; i ++) {
		if (a[i] >= 2 * s) { // x_i、y_i都 >= s
			imin[i] = s;
			imax[i] = a[i] - s;
		} else {  // x_i、y_i都 <= s
			imax[i] = min(a[i], s);
			imin[i] = a[i] - imax[i];
		}
	}
	
	f[2][0] = a[1] * imin[2];
	f[2][1] = a[1] * imax[2];
	for (int i = 3; i <= n - 1; i ++) {
		f[i][0] = min(f[i - 1][0] + imax[i - 1] * imin[i], f[i - 1][1] + imin[i - 1] * imin[i]);
		f[i][1] = min(f[i - 1][0] + imax[i - 1] * imax[i], f[i - 1][1] + imin[i - 1] * imax[i]);
	}
	
	cout << "       ";
	cout << min(f[n - 1][0] + imax[n - 1] * a[n], f[n - 1][1] + imin[n - 1] * a[n]) << "\n";
	// 注意:最后的y_n-1 + a[n]不能在上边那个for循环中进行,
	// 因为n <= 3的时候是无法进入循环的!
}

signed main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int t = 1;
	cin >> t; cin.get();
	while (t --) solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值