0%

2607. 使子数组元素和相等

2607.使子数组元素和相等

题目描述:

给你一个下标从 0 开始的整数数组 arr 和一个整数 k 。数组 arr 是一个循环数组。换句话说,数组中的最后一个元素的下一个元素是数组中的第一个元素,数组中第一个元素的前一个元素是数组中的最后一个元素。

你可以执行下述运算任意次:

  • 选中 arr 中任意一个元素,并使其值加上 1 或减去 1

执行运算使每个长度为 k子数组 的元素总和都相等,返回所需要的最少运算次数。

子数组 是数组的一个连续部分。

数据范围:

$1\le k \le arr.len \le 10^5, 1\le arr[i] \le 10^9$

题解:

可以发现除以 $k$ 余数相同的位置需要修改成一样的,就是一个分组中位数贪心。但是分组时有点不同,比如 [10,9,1,10,5], k = 3。余数为 $0$ 的组为 0,3,1,4,2,(0),把所有的数字都分到了这一组。 $1$ 又可以作为余数为 $1$ 的组,又可以作为余数为 $0$ 的组。可以使用一个 $set$ 标记一下余数为 $p$ 的组是否已经遍历过了,然后求该分组时,一直遍历直到出现了重复元素,在遍历这一组时可能也已经把别的组遍历过了。遍历后面的组时,首先检查是否已经在 $set$ 内。

也可以使用裴蜀定理,一个数组有两个循环节,一个是 $n$ ,一个是 $k$ 。那么 $\gcd(n,k)$ 一定是循环节,而且是最小的循环节。可以只遍历这个循环节分组的。

证明:假设从起点出发,先走了 $x$ 个 $n$ ,又走了 $y$ 个 $k$ ,即 $x\times n + y \times k = z$ ,其中根据裴蜀定理 $z$ 一定是 $p \times \gcd(n, k)$ 。所以走 $p$ 个 $\gcd(n,k)$ 也能到达该位置。

代码:

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
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
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;
long long makeSubKSumEqual(vector<int> &arr, int k)
{
int n = arr.size();
k = __gcd(n, k);
long long ans = 0;
for (int i = 0; i < k; ++i)
{
vector<int> nums;
for (int j = i; j < 2 * n - 1; j += k)
{
nums.emplace_back(j % n);
}

sort(nums.begin(), nums.end(), [&](const int &x, const int &y)
{ return arr[x] < arr[y]; });
int aim = arr[nums[(nums.size()) / 2]];
long long cnt= 0;
for (auto &x : nums)
{
cnt += abs(aim - arr[x]);
arr[x] = aim;
}
ans += cnt;
}
return ans;
}
};
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
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
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;
long long makeSubKSumEqual(vector<int> &arr, int k)
{
int n = arr.size();
// k = __gcd(n, k);
long long ans = 0;
unordered_set<int> mp;
for (int i = 0; i < k; ++i)
{
if(mp.count(i)) continue;
vector<int> nums;
nums.emplace_back(i);
for (int j = (i + k) % n; j != i ; j = (j + k) % n)
{
nums.emplace_back(j);
mp.insert(j);
}
sort(nums.begin(), nums.end(), [&](const int &x, const int &y)
{ return arr[x] < arr[y]; });
int aim = arr[nums[(nums.size()) / 2]];
long long cnt= 0;
for (auto &x : nums)
{
cnt += abs(aim - arr[x]);
arr[x] = aim;
}
ans += cnt;
}
return ans;
}
};