1. 程式人生 > >[HAOI 2008]木棍分割

[HAOI 2008]木棍分割

def its n-1 read script problem int 方法 pla

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]木棍分割