bzoj 1044 [HAOI2008]木棍分割 二分答案+dp

题面

题目传送门

解法

第一问直接二分答案,第二问简单dp

注意空间问题,要用滚动数组

时间复杂度:\(O(nm)\)

代码

#include <bits/stdc++.h>
#define Mod 10007
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int n, m, a[N], s[N], p[N], f[N], sum[N];
bool check(int mid) {
    int sum = 0, tot = 0;
    for (int i = 1; i <= n; i++)
        if (sum + a[i] > mid) tot++, sum = a[i];
            else sum += a[i];
    return tot <= m;
}
int calc() {
    int l = 0, r = 0, ret = 0;
    for (int i = 1; i <= n; i++)
        r += a[i], chkmax(l, a[i]);
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) ret = mid, r = mid - 1;
            else l = mid + 1;
    }
    return ret;
}
int getpos(int x, int sum) {
    int l = 0, r = x, ans = x;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (s[x] - s[mid] <= sum) ans = mid, r = mid - 1;
            else l = mid + 1;
    }
    return ans;
}
int main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++)
        read(a[i]), s[i] = s[i - 1] + a[i];
    int ans1 = calc(), ans2 = 0;
    for (int i = 1; i <= n; i++) p[i] = getpos(i, ans1);
    fill(sum, sum + n + 1, 1);
    for (int j = 1; j <= m + 1; j++) {
        for (int i = 1; i <= n; i++) f[i] = (sum[i - 1] - sum[p[i] - 1] + Mod) % Mod;
        sum[0] = 0;
        for (int i = 1; i <= n; i++) sum[i] = (sum[i - 1] + f[i]) % Mod;
        ans2 = (ans2 + f[n]) % Mod;
    }
    cout << ans1 << ' ' << ans2 << "\n";
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/copperoxide/p/9476507.html