1. 程式人生 > >【bzoj1109】[POI2007]堆積木Klo 動態規劃+樹狀數組

【bzoj1109】[POI2007]堆積木Klo 動態規劃+樹狀數組

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 動態規劃+樹狀數組