【思维构造】Replace With Product—CF1872G

Replace With Product—CF1872G

翻译

给定一个由 n n n个正整数组成的数组 a a a。你需要执行以下操作一次:

  • 选择 2 2 2个整数 l l l r r r 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn),并将子数组 a [ l … r ] a[l \ldots r] a[lr]替换为单个元素:子数组中所有元素的乘积 ( a l ⋅ … ⋅ a r ) (a_l \cdot \ldots \cdot a_r) (alar)

例如,如果对数组 [ 5 , 4 , 3 , 2 , 1 ] [5, 4, 3, 2, 1] [5,4,3,2,1]应用参数 l = 2 , r = 4 l = 2, r = 4 l=2,r=4的操作,数组将变为 [ 5 , 24 , 1 ] [5, 24, 1] [5,24,1]

你的任务是在应用该操作后使数组的总和最大化。找到应用该操作的最佳子数组。
输入

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

每个测试用例的第一行包含一个整数 n n n 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105)——数组 a a a的长度。

每个测试用例的第二行包含 n n n个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an 1 ≤ a i ≤ 1 0 9 1 \le a_i \le 10^9 1ai109)。

保证所有测试用例的 n n n的值的总和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105
输出

对于每个测试用例,输出 2 2 2个整数 l l l r r r 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn)——用于替换乘积的子数组的边界。

如果有多个解决方案,则输出其中任何一个。
注意

在第一个测试用例中,应用参数 l = 2 , r = 4 l = 2, r = 4 l=2,r=4的操作后,数组 [ 1 , 3 , 1 , 3 ] [1, 3, 1, 3] [1,3,1,3]变为 [ 1 , 9 ] [1, 9] [1,9],总和为 10 10 10。很容易看出,通过将任何其他段替换为乘积,总和将小于 10 10 10

在第二个测试用例中,应用参数 l = 3 , r = 4 l = 3, r = 4 l=3,r=4的操作后,数组 [ 1 , 1 , 2 , 3 ] [1, 1, 2, 3] [1,1,2,3]变为 [ 1 , 1 , 6 ] [1, 1, 6] [1,1,6],总和为 8 8 8。很容易看出,通过将任何其他段替换为乘积,总和将小于 8 8 8

在第三个测试用例中,最佳选择是选择 l = r l = r l=r的任何操作,那么数组的总和将保持为 5 5 5,而应用任何其他操作,数组的总和将减少。

C o d e Code Code

#include 
#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;
int a[N];

void solve() {
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}
	
	// 先去除两边的1
	int l = 1, r = n;
	while (l + 1 <= n && a[l] == 1) {
		l ++;
	}
	while (r - 1 >= 1 && a[r] == 1) {
		r --;
	}
	
	// 判断元素的累乘是否大于4e14,如果大于,就直接输出[l, r]
	int ji = 1;
	for (int i = l; i <= r; i ++) {
		if (1.0 * ji * a[i] > 4e14) {
			/*
			  由题意知,数组n个元素的和不会超过4e14
			  所以,当[l, r]内元素的累乘达到2e14的时候,
			  我们就可以确定需要对[l, r]段的元素替换为它们的
			  累乘。
			  证明:
			  假设正确的方法是对[ll, rr]段的元素替换为它们的
			  累乘(l <= ll <= rr <= r),那么增加的大小为:
			  i ∈ [l, ll - 1] ∪ [rr + 1, r]内的元素之和,
			  而减小的大小则至少为:4e14 / 2 >= 2e14,
			  综合来说,则是变小的。
			  证毕。
			 */
			cout << l << ' ' << r << "\n";
			return;
		}
		ji *= a[i];
	}
	
	/*
	  暴力枚举。
	  因为已经确定a数组的累乘不会超过4e14,
	  即使其中 > 1 的数字都是2,数量也一定小于64个,
	  所以这个暴力虽然是O(n^2)的,但实际上消耗的时间很少
	 */
	vector <int> sum(n + 1, 0); // 累加
	vector <int> prod(n + 1, 0); // 累乘
	prod[0] = 1;
	vector <int> one; // 记录元素值为1的下标
	one.push_back(0);
	for (int i = 1; i <= n; i ++) {
		sum[i] = sum[i - 1] + a[i];
		prod[i] = prod[i - 1] * a[i];
		if (a[i] > 1) {
			one.push_back(i);
		}
	}
	
	int L = 1, R = 1, maxn = -1;
	for (int i = 1; i <= sz(one) - 1; i ++) {
		for (int j = i; j <= sz(one) - 1; j ++) {
			int pi = one[i], pj = one[j];
			// 把[pi, pj]区间内的所有数字换成一个数:这些数字的乘积
			if (prod[pj] / prod[pi - 1] + sum[n] - sum[pj] + sum[pi - 1] > maxn) {
				maxn = prod[pj] / prod[pi - 1] + sum[n] - sum[pj] + sum[pi - 1];
				L = pi;
				R = pj;
			}
		}
	}
	
	cout << L << ' ' << R << "\n";
}

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;
}

你可能感兴趣的:(思维构造,c++,算法)