0%

LCP 24. 数字游戏

LCP24.数字游戏

题目描述:

小扣在秋日市集入口处发现了一个数字游戏。主办方共有 N 个计数器,计数器编号为 0 ~ N-1。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组 nums。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。

主办方请小扣回答出一个长度为 N 的数组,第 i 个元素( $0 \le i \lt N$ )表示将 0~i 号计数器 初始 所示数字操作成满足所有条件 nums[a]+1 == nums[a+1],(0 <= a < i) 的最小操作数。回答正确方可进入秋日市集。

由于答案可能很大,请将每个最小操作数对 1,000,000,007 取余。

数据范围:

$1\le nums.len \le 10^5, 1\le nums[i] \le 10^3$

题解:

首先如果满足以 $1$ 为单位递增,那么 $nums[i] - i$ 就是完全相同。转化为 $nums[i] - i$ ,操作最少次使得所有数字都是相等的。这个问题之前见过,就是要转化为中位数。该题需要对每个前缀求中位数,之前的题目是与顺序无关,可以直接排序,然后二分求代价。但是这个题目和数字的顺序有关,需要一遍维护中位数,一边维护小于中卫数的和,大于中位数的和。

可以使用对顶堆动态求中位数,而且可以动态维护两个堆的和。使用一个最大堆 $left$ ,一个最小堆 $right$ 。需要注意的是取模。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Median
{
private:
const static int mod = 1e9 + 7;
priority_queue<int> left; // 堆顶是最大值,小于中位数的
priority_queue<int, vector<int>, greater<int>> right; // 堆顶是最小值,大于等于中位数的
public:
int leftSum;
int rightSum;
Median() : leftSum(0), rightSum(0) {}
vector<int> getNum(vector<int> &nums)
{
int n = nums.size();
vector<int> ans(n);
for (int i = 0; i < n; ++i)
{
push(nums[i]);
ans[i] = getMedian();
}
return ans;
}
void push(int x)
{
// 如果两个堆大小相等
if (left.size() == right.size())
{
// 加入right
if (left.empty() || x >= left.top())
{
right.push(x);
rightSum = (rightSum + x) % mod;
}
else
{
// left弹出最大的到right,加入left
right.push(left.top());
leftSum = (leftSum + x - left.top()) % mod;
rightSum = (rightSum + left.top()) % mod;
left.pop();
left.push(x);
}
}
else // left.size() + 1 == right.size()
{
if (x <= right.top())
{
left.push(x);
leftSum = (leftSum + x) % mod;
}
else
{
left.push(right.top());
leftSum = (leftSum + right.top()) % mod;
rightSum = (rightSum + x - right.top()) % mod;
right.pop();
right.push(x);
}
}
}
int getMedian()
{
return right.top();
}
};
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static long long mod = 1e9 + 7;
const long long INF_LL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
priority_queue<int> left; // 堆顶是最大值,小于中位数的
priority_queue<int, vector<int>, greater<int>> right; // 堆顶是最小值,大于等于中位数的
// 对顶堆求中位数,保持 right.size() <= left.size();
vector<int> numsGame(vector<int> &nums)
{
int n = nums.size();
for (int i = 0; i < n; ++i)
nums[i] -= i;
vector<int> ans(n);
Median median;
for (int i = 0; i < n; ++i)
{
median.push(nums[i]);
ans[i] = ((median.rightSum - (i % 2 == 0) * median.getMedian() - median.leftSum) % mod + mod) % mod;
}
return ans;
}
};