题解 | 最优划分

最优划分

https://www.nowcoder.com/practice/f4aae7b07cd3403996b920aa2de5268c

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, b;
    if (!(cin >> n >> b)) return 0;
    vector<long long> a(n+1);
    for (int i = 1; i <= n; ++i) {
        long long x; cin >> x;
        a[i] = llabs(x); // 取绝对值,确保gcd合法
    }

    if (b > n) {
        // 无法把正长度连续段切成比元素更多的份
        // 依据你们评测要求:可以输出 -1 或把 b 截断到 n。
        // 这里选择直接判无解:
        cout << -1 << "\n";
        return 0;
    }

    const long long NEG = LLONG_MIN / 4;
    vector<long long> prev(n+1, NEG), cur(n+1, NEG);
    prev[0] = 0; // 0 个元素切成 0 段的得分为 0

    for (int k = 1; k <= b; ++k) {
        // 用一个压缩的 gcd 向量表示所有以 i 结尾的后缀 gcd 状态
        vector<pair<long long,long long>> vec; // (gcd, best_base)
        fill(cur.begin(), cur.end(), NEG);

        for (int i = 1; i <= n; ++i) {
            vector<pair<long long,long long>> nvec;
            nvec.reserve(vec.size() + 1);

            // 新开一段,从 i 开始:段内 gcd = a[i],上一层贡献 = prev[i-1]
            if (prev[i-1] != NEG) {
                nvec.emplace_back(a[i], prev[i-1]);
            }

            // 继承旧段:把所有以 i-1 结尾的后缀段延长到 i
            for (auto &p : vec) {
                long long g = std::gcd(p.first, a[i]);
                long long base = p.second;
                if (!nvec.empty() && nvec.back().first == g) {
                    nvec.back().second = max(nvec.back().second, base);
                } else {
                    nvec.emplace_back(g, base);
                }
            }

            // 由于不同来源可能得到相同的 gcd,再做一次线性合并去重(按相同 gcd 合并最大 base)
            // (上面已做“相邻”合并,但这里再稳妥合并所有)
            vector<pair<long long,long long>> merged;
            merged.reserve(nvec.size());
            sort(nvec.begin(), nvec.end(), [](auto &L, auto &R){
                return L.first < R.first; // 按 gcd 升序,便于进一步合并
            });
            for (auto &p : nvec) {
                if (!merged.empty() && merged.back().first == p.first) {
                    merged.back().second = max(merged.back().second, p.second);
                } else {
                    merged.push_back(p);
                }
            }

            // 计算 cur[i] = max(base + gcd)
            long long best = NEG;
            for (auto &p : merged) {
                if (p.second != NEG) best = max(best, p.second + p.first);
            }
            cur[i] = best;

            // 下一轮继承使用的 vec 就是当前 i 的 gcd 压缩列表
            // 为了下一个 i 快速更新,按“不增加随机开销”的方式存为不排序、相邻去重的形态更好。
            // 不过我们此处选择保持“按原顺序+相邻去重”的形式:把 merged 再按 gcd 降序还原并相邻合并。
            // 事实上为简洁起见,直接把 merged(升序且唯一)赋给 vec 也可,下一步取 gcd 仍然正确。
            vec = std::move(merged);
        }
        prev.swap(cur);
    }

    long long ans = prev[n];
    if (ans == NEG) ans = -1; // 若无解
    cout << ans << "\n";
    return 0;
}

全部评论

相关推荐

03-27 01:58
已编辑
西北工业大学 Java
在平静中度过当下:如果这个bg也简历挂的话可能他们现在不缺人了吧,我也是这两天投的,阿里和快手投的岗都是简历秒挂
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务