• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【质数分解+单调栈+快速幂】LeetCode 2818. 操作使得分最大

题目

https://leetcode.cn/problems/apply-operations-to-maximize-score/description/

题解

可以使用 \(O(nlogn)\) 时间复杂度预处理出每个数的质因数个数(不重复计算),记录在数组 \(score\),其中 \(score[x]\) 代表元素 \(x\) 的不重复质因数个数。求质因数个数的过程的时间复杂度是个调和级数,调和级数的复杂度证明可见以下博客:
https://www.cnblogs.com/RomanLin/p/19244514

对于子数组 \(nums[l...r]\) 选择的元素 \(x\) 是在范围 \(l \leq i \leq r\) 种质数分数最大者中下标位置最小者,不妨假设下标位置为 \(i\),那么 \(x\) 就可以贡献 \((i - l + 1) * (r - i + 1)\) 次。

那么,要做的就是计算出数组 \(nums\) 中的每个元素能贡献的次数,随后元素从大到小贡献给答案,直至 \(k\) 次贡献次数被使用完。

对于元素 \(x\),使用单调栈维护出其左侧最相邻的质数分数大于等于 \(x\) 质数分数的位置,再使用单调栈维护出其右侧最相邻的质数分数大于 \(x\) 质数分数的位置。

由于 \(k\) 的数据范围为 \(1 \leq k \leq min(\frac{n \times (n + 1)}{2}, 10^9)\),因此贡献到答案中时,需要使用快速幂进行计算。

参考代码

#define MOD 1000000007
#define N 100001
int idx[N];
int monoStk[N];
int top;

int score[N];// 维护每个元素的质数分数
auto init = []() {
    for (int i = 2; i < N; ++ i) {
        if (!score[i]) {// 若未被访问过,即不存在除本身和 1 以外的因子,则是质数
            for (int j = i; j < N; j += i) {
                ++ score[j];// 质数 i 是元素 j 的质因子
            }
        }
    }
    return 0;
}();

long long qpow(long long base, int exponent) {
    long long res = 1LL;
    while (exponent) {
        if (exponent & 1) {
            res *= base;
            res %= MOD;
        }
        base *= base;
        base %= MOD;
        exponent >>= 1;
    }
    return res;
}

class Solution {
public:
    int maximumScore(vector<int>& nums, int k) {
        long long ans = 1LL;
        int n = nums.size();
        top = -1;
        iota(idx, idx + n, 0);
        vector<int> left(n), right(n);
        // 单调栈比较的是质数分数
        // 先维护出每个元素右侧最相邻的质数分数大于自身质数分数的位置
        for (int i = 0; i < n; ++ i) {
            while (top != -1 && score[nums[monoStk[top]]] < score[nums[i]]) {
                right[monoStk[top]] = i;
                -- top;
            }
            monoStk[++ top] = i;
        }
        while (top != -1) {
            right[monoStk[top]] = n;
            -- top;
        }
        // 再维护出每个元素左侧最相邻的质数分数大于自身质数分数的位置
        for (int i = n - 1; i >= 0; -- i) {
            while (top != -1 && score[nums[monoStk[top]]] <= score[nums[i]]) {
                left[monoStk[top]] = i;
                -- top;
            }
            monoStk[++ top] = i;
        }
        while (top != -1) {
            left[monoStk[top]] = -1;
            -- top;
        }
        // 首先根据元素大小排序,其次根据可贡献次数排序
        sort(idx, idx + n, [&](int i, int j) {
            if (nums[i] != nums[j]) return nums[i] > nums[j];
            return right[i] - left[i] > right[j] - left[j];
        });
        // 计算分数使用的是元素值
        for (int i = 0; i < n && k > 0; ++ i) {
            long long mi = min(1LL * k, (long long)(right[idx[i]] - idx[i]) * (idx[i] - left[idx[i]]));// 计算 nums[idx[i]] 最多可以贡献的次数
            k -= mi;// 减少可贡献次数
            ans = ans * qpow(nums[idx[i]], mi) % MOD;// 对答案贡献 mi 次 nums[idx[i]]
        }
        return ans;
    }
};

posted on 2026-03-14 19:04  RomanLin  阅读(1)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3