[HAOI 2008]木棍分割

Posted NaVi_Awson

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HAOI 2008]木棍分割相关的知识,希望对你有一定的参考价值。

Description

题库链接

\(n\) 根木棍,第 \(i\) 根木棍的长度为 \(L_i\)\(n\) 根木棍依次连结了一起,总共有 \(n-1\) 个连接处。现在允许你最多砍断 \(m\) 个连接处,砍完后 \(n\) 根木棍被分成了很多段,要求满足总长度最大的一段长度最小,并且输出有多少种砍的方法使得总长度最大的一段长度最小。对质数取模。

\(1\leq n\leq 50000,1\leq m\leq \min\{n-1,1000\}\)

Solution

第一问二分,不再赘述。

第二问考虑 \(DP\) 。令 \(f_{i,j}\) 为前 \(i\) 条木棍,划分为 \(j\) 段的方案数。转移我们考虑最后一段怎么分。我们可以预处理出一个数组 \(pre_i\) 表示最后一段最长能分到哪里,显然 \[f_{i,j}=\sum_{k=pre_i}^{i-1}f_{k,j-1}\]

显然这样复杂度是假的。我们可以用前缀和优化,并且珂以记 \(f_{i}\) 为当前枚举到分为 \(j\) 段时,第 \(i\) 条木棍前的方案数。这样直接省去一维。

空间复杂度 \(O(n)\) ;时间复杂度 \(O(nm)\)

Code

//It is made by Awson on 2018.3.3
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('\n'))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 50000, INF = 5e8, yzh = 10007;
void read(int &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}
void print(int x) {if (x > 9) print(x/10); putchar(x%10+48); }
void write(int x) {if (x < 0) putchar('-'); print(Abs(x)); }

int n, m, a[N+5], len, lef[N+5], f[N+5], s[N+5];

bool judge(int x) {
    int cnt = 1, ret = 0;
    for (int i = 1; i <= n; i++) {
    if (a[i] > x) return false;
    if (ret+a[i] > x) ret = a[i], ++cnt; else ret += a[i];
    }
    return cnt <= m;
}
void solve1() {
    int l = 1, r = INF, ans;
    while (l <= r) {
    int mid = (l+r)>>1; if (judge(mid)) ans = mid, r = mid-1; else l = mid+1;
    }
    write(len = ans), putchar(' ');
}
void solve2() {
    int now = 0, ans = 0;
    for (int i = 1; i <= n; i++) {
    a[i] += a[i-1];
    while (a[i]-a[now] > len) ++now;
    lef[i] = now;
    }
    for (int i = 0; i <= n; i++) s[i] = 1;
    for (int len = 1; len <= m; len++) {
    for (int i = 1; i <= n; i++) f[i] = (lef[i] == 0 ? s[i-1] : s[i-1]-s[lef[i]-1]);
    s[0] = 0; for (int i = 1; i <= n; i++) s[i] = (f[i]+s[i-1])%yzh;
    ans = (ans+f[n])%yzh;
    }
    write((ans+yzh)%yzh);
}
void work() {
    read(n), read(m); ++m; for (int i = 1; i <= n; i++) read(a[i]);
    solve1(); solve2();
}
int main() {
    work(); return 0;
}

以上是关于[HAOI 2008]木棍分割的主要内容,如果未能解决你的问题,请参考以下文章

[HAOI 2008]木棍分割

BZOJ1044[HAOI2008]木棍分割 二分+DP

[HAOI2008]木棍分割

[HAOI2008]木棍分割

[BZOJ1044][HAOI2008]木棍分割 二分+贪心+dp+前缀和优化

[HAOI2008]木棍分割