Problem - G - Codeforces

题目大意:有一个长度为n的数组a,要求选出一个区间[l,r],将这个区间内的所有数删掉,替换成这些数的乘积,要求令操作后的整个数组和最大,求操作的区间

1<=n<=2e5;1<=a[i]<=1e9

思路:要求这样的区间,唯一的办法就是求一个前缀和,然后枚举所有区间,找到修改后使和最大的区间,但是枚举区间是n*n的时间复杂度,不能直接对原数组枚举。

我们用贪心的思想考虑,假如我们现在已经对于一个区间[l,r]进行了操作,这其中所有数的乘积为p,如果我们想再把一个数a[i]加入这个区间,我们肯定只放大于1的数,因为1是没有贡献的,那么贡献最少的情况就是a[i]=2,且[r+1,i-1]内的所有数都是1,我们设[l,i]内的数字数量为n,那么如果要把a[i]加入已有区间,新的乘积是p*2,新的和是p+(n-2)+2,新的乘积要大于和才比原来的区间更优,化简不等式得到p>n,而新的乘积是2*p,所以新的乘积要大于2*n,也就是说如果有某一个区间其中的数的乘积>这个区间内的数字数量*2,操作这个区间就比操作这个区间里面的区间要更优。

这样的话,我们建立双指针l=1,r=n,找到最左和最右a[i]>1的位置L,R,如果[L,R]内的数的乘积>2*(R-L+1),[L,R]即为最优的区间。

否则如果没有找到,因为数组最多2e5个数,所有数乘积<=4e5,最坏的情况就是只有2做贡献,那么最多只有18个数有贡献,这样的话我们把这18个数的位置挑出来,就可以利用前缀和,枚举所有区间,找到贡献最大的区间了

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
const ll INF = 1e9;
int n;
ll a[N];
ll sum[N];
void init()
{
	for (int i = 1; i <= n; i++)
	{

	}
}
void solve()
{
	cin >> n;
	init();
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	int l = 1, r = n;
	while (a[l] == 1)
	{//找到最左和最右边有贡献的数
		l++;
	}
	while (a[r] == 1)
	{
		r--;
	}
	ll pro = 1;
	for (int i = l; i <= r; i++)
	{//对这个区间求乘积
		pro *= a[i];
		if (pro > 2 * (r - l + 1))
		{//因为这个乘积数值可能极大,所以只要乘到大于区间长度2倍就直接输出答案
			cout << l << " " << r << endl;
			return;
		}
	}
	vector<ll>num;
	for (int i = 1; i <= n; i++)
	{
		sum[i] = sum[i - 1] + a[i];//对原数组求前缀和
		if (a[i] > 1)
		{//找出所有有贡献的数的位置
			num.push_back(i);
		}
	}
	ll ma = 0;
	ll ansl = 1, ansr = 1;
	for (int i = 0; i < num.size(); i++)
	{
		ll p = 1;
		for (int j = i; j < num.size(); j++)
		{//枚举区间
			p *= a[num[j]];//求这个区间内所有数的乘积
			ll temp = sum[n] - sum[num[j]] + sum[num[i] - 1] + p;//原数组的和减去这个区间内所有数的和,再加上这个区间内的数的乘积
			if (temp > ma)
			{//维护最大值
				ma = temp;
				ansl = num[i], ansr = num[j];
			}
		}
	}
	cout << ansl << " " << ansr << endl;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}
}
09-09 16:21