1 条题解

  • 0
    @ 2026-10-27 4:21:40

    思路与算法

    把圆环复制一遍,枚举切开起点。对长n的一段线性序列做恰m段划分,分别维护前i个数分g段的最小/最大乘积。枚举最后一段起点j,段值为规范化到0..9的前缀差模10,乘上一层值。所有切开起点中再取全局极值。

    正确性说明

    任意圆环m段划分都可以从某条切边展开,枚举起点不会遗漏。固定展开后,最后一段划界j唯一,前部必须是g-1个非空段。每段余数非负,乘同一个段值保持前部最小与最大次序,故两套极值转移分别准确。初态空序列0段乘积为1,保证首段值直接进入。

    复杂度

    O(mn³)时间、O(mn)空间,含每个起点的前缀和、状态表初始化;n≤50,m≤9。

    易错点

    负数求模必须加10规范化。段不能为空;零余数合法,最小乘积常为0。最大值≤9^9,使用64位。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<int> a(2 * n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            a[i + n] = a[i];
        }
        long long lo = 1LL << 60, hi = 0;
        for (int start = 0; start < n; start++) {
            vector<int> pre(n + 1);
            for (int i = 0; i < n; i++)
                pre[i + 1] = pre[i] + a[start + i];
            vector<vector<long long>> mn(m + 1, vector<long long>(n + 1, 1LL << 60)),
                mx(m + 1, vector<long long>(n + 1, -1));
            mn[0][0] = mx[0][0] = 1;
            for (int g = 1; g <= m; g++)
                for (int i = g; i <= n; i++)
                    for (int j = g - 1; j < i; j++)
                        if (mx[g - 1][j] >= 0) {
                            int v = ((pre[i] - pre[j]) % 10 + 10) % 10;
                            mn[g][i] = min(mn[g][i], mn[g - 1][j] * v);
                            mx[g][i] = max(mx[g][i], mx[g - 1][j] * v);
                        }
            lo = min(lo, mn[m][n]);
            hi = max(hi, mx[m][n]);
        }
        cout << lo << "\n" << hi << "\n";
    }
    
    • 1

    信息

    ID
    1996
    时间
    1000ms
    内存
    64MiB
    难度
    普及−
    标签
    递交数
    0
    已通过
    0
    上传者