Codeforces-1692 H: Gambling
题目传送门:Codeforces-1692 H
题目
题目截图
样例描述
题目大意
已经知道了之后 n n n 轮筛子的点数 { x i } \{x_i\} {xi},现在可以选择连续的几轮 x [ l , r ] x_{[l,r]} x[l,r],均猜点数为 a a a,每次猜中就可以把钱翻倍,但若猜错,则会减少一半,问如何选择 l , r , a l,r,a l,r,a,使得收益最大(初始金钱为 1 1 1)。
题目解析
这道题虽然非常简单,但我觉得比较经典。原题解给的是遍历每一种
a
a
a,然后在值为
a
a
a 的地方设为
1
1
1,不为
a
a
a 的地方设为
−
1
-1
−1,之后使用线段树等方法求最大连续子段和。题解这种方法复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn),而且实现复杂。
另一种方法可能更简单。首先,我们肯定要将
a
a
a 与 非
a
a
a 分开讨论,因此要做的第一件事就是把相同的数分为一组,不同的数分成另外的组。之后,我们可以得到每个数的下标序列,之后我们顺序遍历它,使用前缀和的方式,找出获益最大的那个子区间。整个复杂度应该是
O
(
n
)
O(n)
O(n) 的。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
int x[maxn];
int main() {
int t, n;
cin >> t;
while(t--) {
cin >> n;
map<int, vector<int> > M;
for(int i=1; i<=n; ++i) {
cin >> x[i];
M[x[i]].push_back(i);
}
int ans = 1, ansk = x[1], ansl = 1, ansr = 1;
for(map<int, vector<int> >::iterator it = M.begin(); it != M.end(); ++it) {
int key = it->first;
vector<int>& v = it->second;
vector<int> dp = vector<int>(v.size());
dp[0] = 1;
for(int i=1; i<v.size(); ++i)
dp[i] = dp[i-1] + 1 - (v[i] - v[i-1] - 1);
int j = 0;
for(int i=1; i<v.size(); ++i) {
if (ans < dp[i] - dp[j] + 1) {
ans = dp[i] - dp[j] + 1;
ansk = key, ansl = v[j], ansr = v[i];
}
if (dp[j] > dp[i]) j = i;
}
}
cout << ansk << " " << ansl << " " << ansr << endl;
}
return 0;
}