【bzoj1109】[POI2007]堆積木Klo 動態規劃+樹狀數組
阿新 • • 發佈:2017-07-04
pan ret 選擇 data 成了 std cpp name 樹狀數組
題目描述
Mary在她的生日禮物中有一些積木。那些積木都是相同大小的立方體。每個積木上面都有一個數。Mary用他的所有積木壘了一個高塔。媽媽告訴Mary遊戲的目的是建一個塔,使得最多的積木在正確的位置。一個上面寫有數i的積木的正確位置是這個塔從下往上數第i個位置。Mary決定從現有的高塔中移走一些,使得有最多的積木在正確的位置。請你告訴Mary她應該移走哪些積木。
輸入
第一行為一個數n,表示高塔的初始高度。第二行包含n個數a1,a2,...,an,表示從下到上每個積木上面的數。
輸出
註意:請輸出最多有多少點可以處在正確位置
樣例輸入
5
1 1 2 5 4
樣例輸出
3
題解
動態規劃+樹狀數組
設f[i]表示在前i個積木中選擇i最多有多少點處在正確位置。
那麽如果j能夠更新i,需要滿足條件:$j<i\ \&\&\ a_j<a_i\ \&\&\ a_i-a_j\le i-j(j-a_j\le i-a_i)$。
這看似是三個條件的三維偏序問題,而實際上由後兩個條件可以推出第一個條件,可以忽略,就變成了二維偏序問題。
那麽就可以按照a值從小到大排序,就變為了最長不下降子序列問題。可以用樹狀數組來維護。
註意:由於要求$a_j$要嚴格小於$a_i$,所以當它們相等時不應進行更新。這裏為了避免這個問題,將$a_i$相等的i按照$i-a_i$從大到小排序,防止多余的更新。
#include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; struct data { int x , y; }a[N]; int v[N] , tot , f[N] , dp[N] , n; bool cmp1(data a , data b) { return a.y < b.y; } bool cmp2(data a , data b) { return a.x == b.x ? a.y > b.y : a.x < b.x; } void update(int x , int a) { int i; for(i = x ; i <= tot ; i += i & -i) f[i] = max(f[i] , a); } int query(int x) { int i , ans = 0x80000000; for(i = x ; i ; i -= i & -i) ans = max(ans , f[i]); return ans; } int main() { int i , ans = 0; scanf("%d" , &n) , n ++ , a[1].y = 1; for(i = 2 ; i <= n ; i ++ ) scanf("%d" , &a[i].x) , a[i].y = i - a[i].x; sort(a + 1 , a + n + 1 , cmp1) , v[0] = 0x80000000; for(i = 1 ; i <= n ; i ++ ) { if(a[i].y != v[tot]) v[++tot] = a[i].y; a[i].y = tot; } sort(a + 1 , a + n + 1 , cmp2); memset(f , 0x80 , sizeof(f)) , update(a[1].y , 0); for(i = 2 ; i <= n ; i ++ ) dp[i] = query(a[i].y) + 1 , update(a[i].y , dp[i]) , ans = max(ans , dp[i]); printf("%d\n" , ans); return 0; }
【bzoj1109】[POI2007]堆積木Klo 動態規劃+樹狀數組