http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1453、
题目:给定一个大小为100000的数组,里面的数字最大也是100000。现在叫你求出一段子序列,使得他们任意两个数差的绝对值都不能超过k
其实这题的关键是数字的范围,不超过100000,这样的话 ,就可以用线段树整段覆盖了。记dp[i]为以这个数字为结尾的,最长的LIS的多少,开始的时候dp[i]=0,用线段树把他覆盖了。每次插入一个数a[i]的时候,都去找[a[i]-k,a[i]+k]这个区间里的dp最大值,然后修改dp[a[i]] = find()+1即可。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 100000+20; struct data { int L,R,mx; //每个节点,都记录一个区间[L,R]。还有记录区间总和 int mid() {return (L + R)/2;} }SegTree[maxn<<2]; //右移两位,就是*4 void built (int root,int begin,int end) { SegTree[root].L = begin; SegTree[root].R = end;//覆盖区间 if (begin == end) { SegTree[root].mx = 0; return ; } built(root<<1,begin,SegTree[root].mid()); built(root<<1|1,SegTree[root].mid()+1,end); SegTree[root].mx = max(SegTree[root<<1].mx,SegTree[root<<1|1].mx); return ; } void add (int root,int pos,int val) { if (SegTree[root].L == pos && pos == SegTree[root].R) { SegTree[root].mx = val; return ; } if (pos <= SegTree[root].mid()) add(root<<1,pos,val); else if (pos >= SegTree[root].mid()+1) add(root<<1|1,pos,val); SegTree[root].mx = max (SegTree[root<<1].mx,SegTree[root<<1|1].mx); return ; } //[begin,end]是要查询的区间,如果所求区间包含线段树覆盖区间,就可以返回 int find (int root,int begin,int end) //区间查询 { //查询[1,7]的话,左子树区间覆盖了[1,6],也可以直接返回,左子树最大值嘛 if (begin <= SegTree[root].L && end >= SegTree[root].R) return SegTree[root].mx; //覆盖了 if (end <= SegTree[root].mid()) //完全在左子数 return find(root<<1,begin,end); else if (begin >= SegTree[root].mid() + 1) //完全在右子树 return find(root<<1|1,begin,end); else { int Lmax = find(root<<1,begin,end); int Rmax = find(root<<1|1,begin,end); return max(Lmax,Rmax); } } void work () { built(1,0,maxn-20); int n; int k; scanf ("%d%d",&n,&k); for (int i=1;i<=n;++i) { int x; scanf ("%d",&x); int a = max(0,x-k); int b = min(maxn-20,x+k); int t = find(1,a,b); add(1,x,t+1); } printf ("%d\n",find(1,0,maxn-20)); return ; } int main() { #ifdef local freopen("data.txt","r",stdin); #endif int t; scanf ("%d",&t); while(t--) work(); return 0; }
思考:这个复杂度是nlogn的,那么我们是不是又找到了一种求LIS的nlogn算法呢?
不是,说了,这题的关键是数字的大小。不超过100000,才能用线段树这样覆盖,不然的话。是不行的。
LIS中的数组的数字是不确定的。
100000
原文:http://www.cnblogs.com/liuweimingcprogram/p/5754492.html