1. 程式人生 > >BZOJ 1044: [HAOI2008]木棍分割 DP 前綴和優化

BZOJ 1044: [HAOI2008]木棍分割 DP 前綴和優化

前綴 display namespace 木棍分割 狀態轉移方程 mat spl 指針 sca

題目鏈接

咳咳咳,第一次沒大看題解做DP

以前的我應該是這樣的

哇哢哢,這tm咋做,不管了,先看個題解,再寫代碼
終於看懂了,臥槽咋寫啊,算了還是抄吧

第一問類似於noip的那個跳房子,隨便做

這裏重點講第二問

首先,不會做,那就先寫暴力

dp當然得寫dp暴力了

\(f[k][i]\) 表示選擇了k段,到了第i個位置(一共有m+1段)

狀態轉移方程就是\[f[k][i]=f[k][i]+f[k-1][j](sum[i]-sum[j-1]<=ans)\]

for(int i=1;i<=n;++i) {
    if(sum[i]<=ans)
        f[1][i]=1;
}
for (int k=2;k<=m+1;++k) {
    for(int i=k;i<=n;++i) {
        for(int j=i;j>=1;--j) {
            if(sum[i]-sum[j-1] <= ans) {
                f[k][i] += f[k-1][j];
            }
        }
    }
}
三重循環,第一重枚舉k,第二重枚舉i,第三重枚舉j

好了,時間復雜度\(O(n^{2}*m)\),空間復雜度\(O(n*m)\)\(TLE\)(太不良心了,不給暴力分)

空間復雜度的話,很明顯可以滾動數組

考慮第三重循環,是上一次轉移的一段連續的區間

那麽,我們是不是可以把上一他們都前綴和,然後O(1)

那前綴和範圍不明確咋辦?

我們可以用數組O(n)預處理出來

**很明顯的 $i>j 則p[i]>=p[j] \(,指針從1一直往後挪,挪到n** ####**時間復雜度\)(n*m)\(,空間復雜度\)O(n)$,優秀~(≧▽≦)/~**

最後,註意邊界吧

/**************************************************************
    Problem: 1044
    User: 3010651817
    Language: C++
    Result: Accepted
    Time:4528 ms
    Memory:11052 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 5e5 + 7;
const int mod = 10007;
int a[maxn], n, m, l, r;
int sum[maxn], p[maxn];
bool check(int x) {
    int js = 0, tot = 0;
    for (int i = 1; i <= n; ++i) {
        if (tot + a[i] > x) {
            js++, tot = 0;
        }
        tot += a[i];
    }
    if (tot > x) return 0;
    return m >= js;
}
int f[2][maxn];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
    r = sum[n];
    int ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    p[1] = 1;
    for (int i = 2; i <= n; ++i) {
        p[i] = p[i - 1];
        while (sum[i] - sum[p[i] - 1] > ans && p[i] <= i) {
            p[i]++;
        }
    }
    for (int i = 1; i <= n; ++i) p[i] = p[i] >= 2 ? p[i] - 2 : 0;
    for (int i = 1; i <= n; ++i) {
        if (sum[i] <= ans) {
            f[1][i] = 1;
        }
        f[1][i] = f[1][i - 1] + f[1][i];
    }
    int tot = 0;
    for (int i = 2, cnt = 0; i <= m + 1; ++i, cnt ^= 1) {
        for (int j = i; j <= n; ++j) {
            f[cnt][j] = ((f[cnt][j - 1] + f[cnt ^ 1][j - 1]) % mod + mod - f[cnt ^ 1][p[j]]) % mod;
        }
        tot = ((tot + f[cnt][n]) % mod + mod - f[cnt][n - 1]) % mod;
    }
 
    printf("%d %d\n", ans, tot );
    return 0;
}

BZOJ 1044: [HAOI2008]木棍分割 DP 前綴和優化