|
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3349
/**//* 题意: 给定一个d(0 <= d <= 10^8)和(N <= 10^5)的数列,求最长的特殊子序列, 所谓特殊子序列就是相邻元素之间的绝对值之差小于等于d。
解法: 动态规划+线段树
思路: 这题又是一个动态规划,状态转移方程很容易想到: dp[ val[i] ] = 1 + max( dp[ val[i] - d ] dp[ val[i] + d ] ) dp[j] 表示以j为止的最长特殊子序列的值,这样就可以维护一个区间,每次 查询和当前数绝对值差小于等于d的数组成的区间,将最大值+1更新到当前数 对应的位置上,利用线段树每次更新和查询都是log(n)。 */
#include <iostream> #include <cstdio> #include <cstring> using namespace std;
#define maxn 600010
int n, d; int val[maxn];
struct Tree { int Max; int son[2];
void clear() { son[0] = son[1] = -1; Max = 0; } }T[maxn*4]; int tot;
int Max(int a, int b) { return a > b ? a : b; } int Min(int a, int b) { return a < b ? a : b; }
void Query(int root, int nx, int ny, int l, int r, int& ans) { if(nx > r || ny < l || root == -1 || T[root].Max <= ans) return ; if(nx <= l && r <= ny) { ans = Max(ans, T[root].Max); return ; } int mid = (l + r) >> 1; Query(T[root].son[0], nx, ny, l, mid, ans); Query(T[root].son[1], nx, ny, mid+1, r, ans); }
void Insert(int& root, int nPos, int l, int r, int val) { if(nPos < l || nPos > r) return ; if(root == -1) { root = tot++; T[root].clear(); } T[root].Max = Max(val, T[root].Max);
if(l == nPos && nPos == r) { return ; }
int mid = (l + r) >> 1; Insert(T[root].son[0], nPos, l, mid, val); Insert(T[root].son[1], nPos, mid+1, r, val); }
int main() { int i; int MMin, MMax; while(scanf("%d %d", &n, &d) != EOF) { for(i = 0; i < n; i++) { scanf("%d", &val[i]); if(i) { MMin = Min(MMin, val[i]); MMax = Max(MMax, val[i]); }else { MMin = val[i]; MMax = val[i]; } } tot = 0; int root = -1; int ans = 1;
for(i = 0; i < n; i++) { int l = (val[i] - d) < MMin ? MMin : (val[i] - d); int r = (val[i] + d) > MMax ? MMax : (val[i] + d); int MM = 0; Query(root, l, r, MMin, MMax, MM); Insert(root, val[i], MMin, MMax, MM + 1); if(MM + 1 > ans) ans = MM + 1; }
printf("%d\n", ans); } return 0; }
|