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
解题方法: 我们先列一下普通的DP方程,
然后这里就是3个限制条件了?CDQ三维偏序?翻了翻题解,发现神思路的题目。观察三个限定条件。
容易发现已知2,3可以推出1。 而1代表的是这n个数的排列顺序。 而2的条件即为最长上升子序列。 所以我们不妨把3看做这n个数的重新排列法则,之后满足2的条件即可。 所以我们只需要按照j−a[j]<=i−a[i]把n个数重新排列,接着求一个最长上升子序列长度即可。 需要注意的是,如果i−a[i]<0的话,那么显然这个数不可能与C序列中的某个数对应上,直接跳过即可。
LIS可以二分也可以用树状数组的方法,PO爷用的树状数组的方法,可以看PO爷博客,蒟蒻写了一个代码和博主几乎一样的二分版本的代码。
#include <bits/stdc++.h>using namespace std;const int maxn = 100010;const int maxm = 1000010;struct node{ int x, y; node(){} node(int x, int y) : x(x), y(y) {} bool Operator < (const node &rhs) const{ if(x == rhs.x) return y < rhs.y; return x < rhs.x; }}b[maxn];int n, cnt, ans, d[maxm], a[maxn];int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); if(i - a[i] < 0) continue; b[++cnt].x = i - a[i], b[cnt].y = a[i]; } sort(b + 1, b + cnt + 1); memset(d, 0x3f, sizeof(d)); for(int i = 1; i <= cnt; i++) { int l = 1, r = ans, len = 0; while(l <= r){ int mid = (l + r) / 2; if(b[i].y > d[mid]){ len = mid, l = mid + 1; } else{ r = mid - 1; } } ans = max(ans, len + 1); d[len + 1] = min(d[len + 1], b[i].y); } cout << ans << endl; return 0;}新闻热点
疑难解答