2607. 使子数组元素和相等

发布时间 2023-04-09 01:14:01作者: lxy_cn

题目链接:2607. 使子数组元素和相等

方法:分组 + gcd + 中位数

解题思路

题意:将\(arr\)中某个元素\(+1\)\(-1\),使得任意长度为\(k\)的子数组的元素总和相等,且总操作数最少;
1、首先考虑数组\(arr\)为非循环数组:

  • 任意\(k\)长的子数组总和相等,则有下述情形,依次可以将\(arr\)分组,使得组内的元素相同,此时对组内元素操作时,为了使得操作次数最小,应该使得每个组内元素与组内的中位数相同,此时所需次数最少。
/*
      a[0] + a[1] + .. + a[k - 1]
    = a[1] + a[2] + ... + a[k] 
    = a[2] + a[3] + ... + a[k + 1]
    ...
    = a[n - k] + ... + a[n - 1]
=>
    a[0] = a[k] = a[2k] = ... = a[xk], xk < n
    a[1] = a[k + 1] = a[2k + 1] = ... = a[xk + 1], xk + 1 < n
    a[2] = a[k + 2] = a[2k + 2] = ... = a[xk + 2], xk + 2 < n
    ...
*/

2、考虑数组\(arr\)为循环数组:

  • 在上述的情况下,增加循环条件,每一组遍历到当前元素已经被添加过,因此需要有一个\(isvisit\)数组,判断当前元素是否已经被添加,然后使得每个组内元素与组内的中位数相同,输出总的操作次数。
/*
      a[0] + a[1] + .. + a[k - 1]
    = a[1] + a[2] + ... + a[k] 
    = a[2] + a[3] + ... + a[k + 1]
    ...
    = a[n - k] + ... + a[n - 1]
    = a[n - k + 1] + ... + a[n - 1] + a[0]
    = a[n - k + 2] + ... + a[n - 1] + a[0] + a[1]
    ...
=>
    a[0] = a[k] = a[2k] = ... = a[xk % n]
    a[1] = a[k + 1] = a[2k + 1] = ... = a[(xk + 1) % n]
    a[2] = a[k + 2] = a[2k + 2] = ... = a[(xk + 2) % n]
    ...
*/

代码

class Solution {
public:
    long long makeSubKSumEqual(vector<int>& arr, int k) {
        int n = arr.size();
        long long ans = 0;
        vector<int> isvisit(n); 
        for (int i = 0; i < k; i ++ ) {
            if (isvisit[i]) continue; // 被添加过则跳过
            vector<int> cur;
            int j = i;
            while (!isvisit[j]) { // isvisit[j] = 1,表明回到某一个被添加过的元素,终止遍历
                isvisit[j] = 1;
                cur.push_back(arr[j]);
                j = (j + k) % n;
            }
            sort(cur.begin(), cur.end());
            for (auto &num : cur) {
                ans += abs(num - cur[cur.size() / 2]);
            }
        }    
        return ans;
    }
};

复杂度分析

时间复杂度:\(O(nlogn)\)
空间复杂度:\(O(n)\)

优化

  • 裴蜀定理简介
    \(a,b\)是整数,且\(gcd(a,b)=d\),那么对于任意的整数\(x,y\)\(ax+by\)都一定是\(d\)的倍数,特别地,一定存在整数\(x,y\),使\(ax+by=d\)成立。

由于本题为循环数组,所以周期除了\(k\)还有\(n\),那么对于\(a[i]\)有,\(a[i] = a[i + xk + yn]\)。根据上述定理,有\(a[i] = a[i + gcd(k, n)]\),那么只需要判断。

/*
    k = gdc(k, n)
    a[0] = a[k] = a[2k] = ... = a[xk], xk < n
    a[1] = a[k + 1] = a[2k + 1] = ... = a[xk + 1], xk + 1 < n
    a[2] = a[k + 2] = a[2k + 2] = ... = a[xk + 2], xk + 2 < n
    ...
*/
class Solution {
public:
    long long makeSubKSumEqual(vector<int>& arr, int k) {
        int n = arr.size();
        long long ans = 0;
        k = gcd(n, k);
        for (int i = 0; i < k; i ++ ) {
            vector<int> cur;
            for (int j = i; j < n; j += k) cur.push_back(arr[j]);
            nth_element(cur.begin(), cur.begin() + cur.size() / 2, cur.end()); // 快速选择降低复杂度
            for (auto &num : cur) {
                ans += abs(num - cur[cur.size() / 2]);
            }
        }    
        return ans;
    }
};

复杂度分析

时间复杂度:\(O(n)\)
空间复杂度:\(O(n)\)