CF1428G2 Lucky Numbers (Hard Version)

Part 1:一些发现

首先我们注意到,每个数位可以几乎独立地计算贡献,不同数位的之间的联系只在于总和 \(n\)

同时还容易发现,把两个同数位上的 \(3,3\)\(3,6\),合并成一个必定不劣,可能更优
比如说:假若存在两个数同数位上分别是 \(3,3\),我们将它们合并成一个 \(6\) 必定不劣,剩下的一个数有了更大的空间。\(3,6\) 也同理。

Part2:维护方法

考虑 \(n\) 不大,考虑直接将 \(n\) 记进状态 DP。\(f_{i,j}\) 表示,考虑了前 \(i\) 高位,当前总大小为 \(j\) 的最大权值

一个理想策略是,所有当前数位上全是 \(3\) 的倍数。然而这不现实——因为题目要求选满 \(n\),而不是要求总和 \(\leq n\)

对此,我们退而求其次,留一个空位给非 \(3\) 的倍数。这样的正确性是因为任意两个不是 \(3\) 倍数的数,必定可以调整出至少一个 \(3\) 的倍数

每次转移枚举这样的空位上填什么。对于剩下的位置,假若把题目给的 \(F_i\) 看做一个物品的话,至多能有 \(3(k-1)\) 个物品,做二进制分组即可。

时间复杂度 \(O(10n\log k)\),常数极小,容易通过。

Part3:代码

#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
#define fi first
#define se second
#define eb emplace_back
#define Sz(v) ((int)(v).size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int Nn = 1e6, N = Nn + 10;
const ll INFLL = 1e18;
int k, q;
ll F[6], f[7][N];
int pw[] = {1, 10, 100, 1000, 10000, 100000, 1000000};
template<typename T>
void ChkMax(T &x, const T &y) {
    if (y > x) x = y;
}
void DP() {
    FL(i, 0, 6) {
        fill(f[i], f[i] + Nn + 2, -INFLL);
    }
    vector<int> t;
    {
        int tk = min(Nn, (k - 1) * 3);
        for (int i = 1; i <= tk; i <<= 1) {
            t.emplace_back(i);
            tk -= i;
        }
        if (tk) t.emplace_back(tk);
    }
    f[6][0] = 0;
    FR(i, 5, 0) {
        FL(j, 0, Nn) {
            FL(k, 0, 9) {
                if (f[i + 1][j] < 0) continue;
                ll tj = j + (ll)k * pw[i];
                ll tc = f[i + 1][j] + (k % 3? 0 : (k / 3) * F[i]);
                ChkMax(f[i][tj], tc);
            }
        }
        for (int x: t) {
            ll R = Nn - (ll)x * pw[i] * 3;
            if (R < 0) continue;
            FR(j, R, 0) {
                ll nj = j + (ll)x * pw[i] * 3;
                ChkMax(f[i][nj], f[i][j] + x * F[i]);
            }
        }
    }
}
int main() {
    scanf("%d", &k);
    FL(i, 0, 5) {
        scanf("%lld", &F[i]);
    }
    DP();
    scanf("%d", &q);
    while (q--) {
        int x;
        scanf("%d", &x);
        printf("%lld\n", f[0][x]);
    }
    return 0;
}
posted @ 2025-07-05 08:31  徐子洋  阅读(13)  评论(0)    收藏  举报