您的位置:首页 > 其它

CSU 1453: 平衡序列 学会线段树后必做

2016-08-09 20:12 309 查看
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;
}


View Code
思考:这个复杂度是nlogn的,那么我们是不是又找到了一种求LIS的nlogn算法呢?

不是,说了,这题的关键是数字的大小。不超过100000,才能用线段树这样覆盖,不然的话。是不行的。

LIS中的数组的数字是不确定的。

100000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: