P13009题解

传送门:https://www.luogu.com.cn/problem/P13009

考虑向下取整数学意义。不妨令 \(m=k*a[i]+b \ (0 \leq b< a[i])\)

\(\lfloor\frac{m}{a[i]}\rfloor =k\)

我们再进行第二次做除法下取整,即求 \(\lfloor\frac{m}{k}\rfloor\)

\(k \leq a[i]\) 时:

  • \(b < k\) ,余数不足以向前进位,显然有 \(\lfloor\frac{m}{k}\rfloor =a[i]\)

  • \(b \ge k\) ,存在 \(t>0\) ,使得 \(\lfloor\frac{m}{a[i]}\rfloor = k*(a[i]+t)+b-kt\) ,其中 \(b-kt<k\) 。则 \(\lfloor\frac{m}{k}\rfloor =a[i]+t\)

\(k > a[i]\) 时:

  • \(k>a[i]>b\),余数不足以进位,显然 \(\lfloor\frac{m}{a[i]}\rfloor =k\)

在第二次做除法下取整后,我们用以上方法表达的余数必定 \(<a[i]\)\(<\lfloor\frac{m}{a[i]}\rfloor\) 。最终得到的整除结果在 \(k\)\((a[i]+t)\) 中两者中循环( \(t\) 可能为 \(0\) )。

由此我们发现得到的严格最大结果可能是第一次整除,或第二次整除。

问题转化为区间覆盖问题。我们需要的最大覆盖次数为 \(2\) ,而如果一个格子与一段连通块(或只有自身)一起被覆盖,覆盖次数显然不会超过 \(2\) ,而一个格子若同时被两个连通块覆盖而本身不需要更多次的覆盖,则可以忽略此格子分别选择其他两个连通块,操作次数均为 \(2\) 次,由此我们可以证明任何格子不覆盖超过 \(2\) 次即可获得最优解。

一个格子可能必须被覆盖 \(2\) 次或 \(1\) ,或可以被覆盖 \(2\) 次 或 \(1\) 次,或任意次数均可。贪心显然无法兼顾如此多约束,但一个格子选取次数定后,后一个格子可以选择跟随选取,或少选以及多选,如果少选无需付出次数,多选则额外付出超过的次数,于是用动态规划解决这个问题。

\(f[i][j]\) 表示第 \(i\) 位覆盖 \(j\) 次的最小代价。

\[\begin{cases} f[0][0]=0 \\ f[i][j]=f[i-1][p]+ (p-j>0)\ ? \ p-j : 0 &(i>0且f[i][j]合法) \\ f[i][j] = inf \ & (i>0且f[i][j]不合法)\end{cases} \]

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

ll a[100001];
int f[100001][3];

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        int n;
        ll m, ans = 0;
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) cin >> a[i];
        for (int i = 0; i <= 2; ++i) for (int j = 0; j <= n; ++j) f[j][i] = 0x3f3f3f3f;
        f[0][0] = 0;
        for (int i = 1; i <= n; ++i) {
            if (m / a[i] > a[i]) f[i][1] = min(f[i - 1][1], min(f[i - 1][2], f[i - 1][0] + 1));
            else if (m / a[i] == a[i])
                f[i][0] = min(min(f[i - 1][0], f[i - 1][1]), f[i - 1][2]), f[i][1] =
                        min(f[i - 1][1], min(f[i - 1][2], f[i - 1][0] + 1)), f[i][2] = min(
                            min(f[i - 1][1] + 1, f[i - 1][2]), f[i - 1][0] + 2);
            else if (m / (m / a[i]) > a[i]) f[i][2] = min(min(f[i - 1][1] + 1, f[i - 1][2]), f[i - 1][0] + 2);
            else
                f[i][0] = min(min(f[i - 1][0], f[i - 1][1]), f[i - 1][2]), f[i][2] = min(
                    min(f[i - 1][1] + 1, f[i - 1][2]), f[i - 1][0] + 2);
            ans += max(m / a[i], m / (m / a[i]));
        }
        cout << ans << ' ' << min(f[n][0], min(f[n][1], f[n][2])) << '\n';
    }
    return 0;
}
posted @ 2025-07-12 17:05  Jefferyzzzz  阅读(18)  评论(0)    收藏  举报