1. 程式人生 > >BZOJ 1109: [POI2007]堆積木Klo

BZOJ 1109: [POI2007]堆積木Klo

Description

  Mary在她的生日禮物中有一些積木。那些積木都是相同大小的立方體。每個積木上面都有一個數。Mary用他的
所有積木壘了一個高塔。媽媽告訴Mary遊戲的目的是建一個塔,使得最多的積木在正確的位置。一個上面寫有數i
的積木的正確位置是這個塔從下往上數第i個位置。Mary決定從現有的高塔中移走一些,使得有最多的積木在正確
的位置。請你告訴Mary她應該移走哪些積木。

Input

  第一行為一個數n,表示高塔的初始高度。第二行包含n個數a1,a2,…,an,表示從下到上每個積木上面的數。
(1<=n<=100000,1<=ai<=1000000)。

Output

  注意:請輸出最多有多少點可以處在正確位置

Sample Input

5

1 1 2 5 4

Sample Output

3

分析

我們設f[i]表示前i位能到達自己位置的數最多有多少,難麼我們就可以得到一個dp方程:

f[i]=max(f[j]+1)(j<ia[j]<a[i]a[i]a[j]ij)
我們觀察一下三個限制條件
1.j<i
2.a[j]<a[i]
3.a[i]a[j]ijja[j]ia[i]
我們按照ia[i]作為第一關鍵字a[i]作為第二關鍵字,做一遍最長上升子序列即可

程式碼

#include <bits/stdc++.h>
#define N 1000100 using namespace std; int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();} return x * f; } struct NOTE { int
x,y; friend bool operator < (NOTE a,NOTE b) { if (a.x == b.x) return a.y < b.y; return a.x < b.x; } }num[N]; int tot; int p[N]; int main() { int n = read(); for (int i = 1; i <= n; i++) { int x = read(); if (x > i) continue; num[++tot].x = i - x; num[tot].y = x; } std::sort(num + 1, num + tot + 1); memset(p,127,sizeof(p)); int ans = 0; for (int i = 1; i <= tot; i++) { int l = 1, r = ans, mn = 0; while (l <= r) { int mid = (l + r) >> 1; if (num[i].y > p[mid]) mn = mid, l = mid + 1; else r = mid - 1; } ans = std::max(ans, mn + 1); p[mn + 1] = std::min(p[mn + 1], num[i].y); } printf("%d\n",ans); }