1. 程式人生 > >BZOJ3594 [Scoi2014]方伯伯的玉米田 【樹狀數組優化dp】

BZOJ3594 [Scoi2014]方伯伯的玉米田 【樹狀數組優化dp】

modify har odi 後綴 () div red ring cls

題目鏈接

BZOJ3594

題解

dp難題總是想不出來,,

首先要觀察到一個很重要的性質,就是每次拔高一定是拔一段後綴
因為如果單獨只拔前段的話,後面與前面的高度差距大了,不優反劣

然後很顯然可以設出\(f[i][j]\)表示前\(i\)個玉米,第\(i\)棵必須選,且共拔高了\(j\)次的最大值
由之前的性質,我們知道\(f[i][j]\)狀態中\(i\)的高度是\(h[i] + j\)
所以可以的到狀態轉移方程:
\[f[i][j] = max\{f[k][l]\} + 1 \quad [k < i] \quad [h[i] + j \ge h[k] + l]\]
可以用二維樹狀數組維護

復雜度\(O(nK + nlog^2n)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int #define lbt(x) (x & -x) using namespace std; const int maxn = 10005,maxm = 505,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1
; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int h[maxn],n,K; int s[maxn][maxm],N = 5503,M = 503; void modify(int u,int p,int v){ for (int i = u; i <= N; i += lbt(i)) for (int j = p; j <= M; j += lbt(j)) s[i][j] = max(s[i][j],v); } int query(int u,int p){ int re = 0; for (int i = u; i; i -= lbt(i)) for (int j = p; j; j -= lbt(j)) re = max(re,s[i][j]); return re; } int main(){ n = read(); K = read(); for (int i = 1; i <= n; i++) h[i] = read(); int ans = 0; for (int i = 1; i <= n; i++){ for (int j = K; j >= 0; j--){ int t = query(h[i] + j,j + 1) + 1; ans = max(ans,t); modify(h[i] + j,j + 1,t); } } printf("%d\n",ans); return 0; }

BZOJ3594 [Scoi2014]方伯伯的玉米田 【樹狀數組優化dp】