Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Leetcode 2818. Apply Operations to Maximize Score #284

Open
Woodyiiiiiii opened this issue Aug 16, 2023 · 0 comments
Open

Leetcode 2818. Apply Operations to Maximize Score #284

Woodyiiiiiii opened this issue Aug 16, 2023 · 0 comments

Comments

@Woodyiiiiiii
Copy link
Owner

Woodyiiiiiii commented Aug 16, 2023

2818. Apply Operations to Maximize Score

2818. Apply Operations to Maximize Score

单调栈题单:
496. Next Greater Element I
503. Next Greater Element II
556. Next Greater Element III
2454. Next Greater Element IV (未AC)
2104. Sum of Subarray Ranges

贡献法题单:
907. Sum of Subarray Minimums
1856. Maximum Subarray Min-Product
2281. Sum of Total Strength of Wizards (未AC)

我一开始以为是用堆先记录所有长度为1的子数组,然后依次取最大,然后扩展,就像之前做过的那样。但发现k特别大,所以要对k进行处理,要结合数组长度n有效忽略k。所以这个方法不可行了。

后来发现可以用贡献法,先求每个元素能够成为最大不同质因数个数的子数组个数,然后从最大元素开始,依次往下计算,这样就能从n出发,模糊了k的大小。这也是从时间复杂度破题

那么卡点在于如何在O(n)范围内求每个元素辐射的范围。一开始我还想着用TreeMap来做,受了第三题的影响,但很明显有问题。思考下,我们求的是每个元素不同质因数大小最大范围,假设从左往右遍历,对于每个元素,要求的是最左边能到达的不同质因数大小大于等于该元素的位置,到底用什么结构呢?单调栈。我对这个知识点不熟悉,有些遗忘了。

单调栈只能有一个方向,所以我需要遍历两遍数组,求的左右范围,其中范围计算是leftLen += (leftLen * rightLen) 包括自己本身

然后结合,从最大元素开始计算,然后用BigInteger(不是BigDecimal)快速幂,取模计算。

综合题目,很有学习价值。涉及知识点:

  1. 贡献法
  2. 单调栈
  3. 求质因数
  4. 快速幂
import java.math.BigInteger;

class Solution {
    public int maximumScore(List<Integer> nums, int k) {
        int n = nums.size();
        int[] counts = new int[n];
        for (int i = 0; i < n; i++) {
            counts[i] = cntOfDistinctPF(nums.get(i));
        }
        final int MOD = (int) 1e9 + 7;
        BigInteger ans = BigInteger.ONE;

        // init - left to right
        long[] sums = new long[n];
        LinkedList<Integer> stack = new LinkedList<>();     // 最小栈
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && counts[i] > counts[stack.peek()]) {
                stack.pop();
            }
            if (stack.isEmpty()) {
                sums[i] = i + 1;
            } else {
                sums[i] = i - stack.peek();
            }
            stack.push(i);
        }
        // right to left
        stack.clear();
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && counts[i] >= counts[stack.peek()]) {
                stack.pop();
            }
            if (stack.isEmpty()) {
                sums[i] += (sums[i] * (n - i - 1));
            } else {
                sums[i] += (sums[i] * (stack.peek() - i - 1));
            }
            stack.push(i);
        }

        PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o2[0] - o1[0]);
        for (int i = 0; i < n; i++) {
            pq.add(new int[]{nums.get(i), i});
        }

        while (k > 0 && !pq.isEmpty()) {
            int[] poll = pq.poll();
            long cnt = Math.min(k, sums[poll[1]]);
            BigInteger cur = BigInteger.valueOf(poll[0]).modPow(BigInteger.valueOf(cnt), BigInteger.valueOf(MOD));
//            BigInteger cur = BigInteger.valueOf(poll[0]).pow((int) cnt);
            ans = ans.multiply(cur).mod(BigInteger.valueOf(MOD));
            k -= (int) cnt;
        }

        return ans.intValue();
    }

    private int cntOfDistinctPF(int num) {
        int cnt = 0;
        for (int i = 2; i * i <= num; i++) {
            if (num % i == 0) {
                cnt++;
//                System.out.print(i + " ");
                while (num % i == 0) {
                    num /= i;
                }
            }
        }
        if (num > 1) {
//            System.out.print(num + " ");
            cnt++;
        }
//        System.out.println();
        return cnt;
    }
}
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant