题意:给定一个长度为N的序列,现在要求给出一个最长的序列满足序列中的元素严格上升并且相邻两个数字的下标间隔要严格大于d。

分析:

1.线段树

由于给定的元素的取值范围为0-10^5,因此维护一棵线段树,其中[l, r]的信息表示处理完前k个数时,序列最大元素落在[l, r]区间最长上升子序列的长度。从前往后处理给定的数组,处理到第 i 号元素时,更新第 i - d 号元素,这样就能够保证最长上升的序列间隔大于d,更新是需要更新到叶子节点的,但这里更新是单点更新,每次更新的位置是该元素的值,信息就是到该点的最长上升长度。

其实仔细分析可以发现这个解法其实是经典的O(n^2)的算法的改进,那个算法需要遍历之前的更新信息比较相对大小,因此也不能简单的维护前缀最值,而线段树由于节点是值信息,查询的时候就不要去检验之前大小关系,加之线段树有能够动态区间求解各种信息,时间复杂度就这么被降下来了。当然如果取值范围较大,只要N不大还能够离散化。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define lch (p<<1)
#define rch (p<<1|1)
using namespace std; const int N = ;
int n, d;
int seq[N];
int alen[N]; struct Node {
int l, r;
int len;
}e[N*]; void build(int p, int l, int r) {
e[p].l = l, e[p].r = r, e[p].len = ;
if (l != r) {
int mid = (l + r) >> ;
build(lch, l, mid), build(rch, mid+, r);
}
} void push_up(int p) {
e[p].len = max(e[lch].len, e[rch].len);
} void modify(int p, int x, int val) {
if (e[p].l == e[p].r) e[p].len = max(e[p].len, val);
else {
int mid = (e[p].l + e[p].r) >> ;
if (x <= mid) modify(lch, x, val);
else modify(rch, x, val);
push_up(p);
}
} int query(int p, int l, int r) {
if (e[p].l == l && e[p].r == r) return e[p].len;
else {
int mid = (e[p].l + e[p].r) >> ;
if (r <= mid) return query(lch, l, r);
else if (l > mid) return query(rch, l, r);
else return max(query(lch, l, mid), query(rch, mid+, r));
}
} int main() {
while (scanf("%d %d", &n, &d) != EOF) {
build(, , ); // 建立0-10^5的线段树
int ret = ;
for (int i = ; i <= n; ++i) {
scanf("%d", &seq[i]);
if (seq[i] > ) ret = max(ret, alen[i]=query(, , seq[i]-)+);
else ret = max(ret, alen[i] = );
if (i-d>=) modify(, seq[i-d], alen[i-d]);
}
printf("%d\n", ret);
}
return ;
}

2.经典O(nlogn)LIS变种

经典的算法在数组中保留都是下标节点比当前点小的节点,因为从前往后处理也因为经典的算法其实处理的是间隔d=0的特殊情况,那么稍微进行一下推广,当我们处理完第 i 个元素只是把第 i - d 号元素放到数组中,放入的位置就是以前求出来的最长上升子序列长度,当然放入的时候要比较一下是否需要替换。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std; const int N = ;
int n, d;
int seq[N];
int alen[N]; void solve() {
vector<int>vt;
vector<int>::iterator it;
int ret = ;
for (int i = ; i <= n; ++i) {
it = lower_bound(vt.begin(), vt.end(), seq[i]);
if (it == vt.end()) alen[i] = vt.size()+;
else alen[i] = it-vt.begin()+;
if (i-d >= ) {
if (vt.size() == alen[i-d]-) vt.push_back(seq[i-d]);
else if (vt[alen[i-d]-] > seq[i-d]) vt[alen[i-d]-] = seq[i-d];
}
ret = max(ret, alen[i]);
}
printf("%d\n", ret);
} int main() {
while (scanf("%d %d", &n, &d) != EOF) {
for (int i = ; i <= n; ++i) {
scanf("%d", &seq[i]);
}
solve();
}
return ;
}

HDU-4521 小明系列问题——小明序列 间隔限制最长上升子序列的更多相关文章

  1. hdu 4521 小明系列问题——小明序列 线段树+二分

    小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Pro ...

  2. hdu 4521 小明系列问题——小明序列(线段树+DP或扩展成经典的LIS)

    小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Tot ...

  3. 小明系列问题――小明序列(LIS)

    小明系列问题――小明序列 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit ...

  4. hdu----(4521)小明系列问题——小明序列

    小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tota ...

  5. 小明系列问题——小明序列(Lis 相距大于d的单调上升子序列)

    小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Tot ...

  6. 2018.07.08 hdu4521 小明系列问题——小明序列(线段树+简单dp)

    小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Proble ...

  7. HDU 4521 小明系列问题——小明序列 (线段树 单点更新)

    题目连接 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来 ...

  8. hdu 4521 小明系列问题——小明序列(线段树 or DP)

    题目链接:hdu 4521 本是 dp 的变形,却能用线段树,感觉好强大. 由于 n 有 10^5,用普通的 dp,算法时间复杂度为 O(n2),肯定会超时.所以用线段树进行优化.线段树维护的是区间内 ...

  9. HDU 4521 小明系列问题——小明序列 (线段树维护DP)

    题目地址:HDU 4521 基本思路是DP.找前面数的最大值时能够用线段树来维护节省时间. 因为间隔要大于d. 所以能够用一个队列来延迟更新,来保证每次询问到的都是d个之前的. 代码例如以下: #in ...

随机推荐

  1. 在使用Myeclipse时,用Tomcat添加部署项目的时候报错,或启动tomcat报错

    the selected server is enabled,but is not configured properly.deployment to it will not be permitted ...

  2. POSIX字符类

    POSIX字符类需要用引号,或双括号[[]]括起来: [:alnum:]:匹配字面和数字字符.等同于A~Z,a~z,0~9 [:alpha:]:匹配字母字符.等同于A~Z,a~z [:blank:]: ...

  3. myeclipse 常用快捷键总结

    1 shift+enter 不管鼠标在当前行的什么位置,重新开启一行(向下) 2 shift+ctrl+enter 不管鼠标在当前行的什么位置,重新开启一行(向上) 3 Ctrl+D     删除一行 ...

  4. 使用compile_scripts.php脚本,生成lua打包的zip,解决加密问题

    @echo off set DIR=%~dp0 set TEMPLATE_ROOT=%DIR%.. echo %TEMPLATE_ROOT%\quick\bin\win32\php.exe echo ...

  5. CI分页,搜索之后翻页不能用问题

     最近在学习用php的CI框架写一个自己的CMS,遇到了些问题.其中一个就是CI分页的时候,我的URL带有其他参数,才能查出我想要的数据.于是我翻遍了谷歌度娘,终于找到了解决办法,和我想的差不多,就贴 ...

  6. C Looooops(扩展欧几里得)

    C Looooops Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 20128 Accepted: 5405 Descripti ...

  7. Proud Merchants

    Proud Merchants Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) To ...

  8. Python学习笔记-Day2-Python基础之字典操作

    字典的常用操作包括但不限于以下操作: 字典的字典的索引,新增,删除,循环,长度等等 这里将对列表的内置操作方法进行总结归纳,重点是以示例的方式进行展示. 使用type获取创建对象的类 type(dic ...

  9. vsftp 使用匿名帐号登陆

    1.正常安装. 2.改配置文件:vi /etc/vsftpd/vsftpd.conf #允许匿名用户登录FTP anonymous_enable=YES #设置匿名用户的登录目录(如需要,需自己添加并 ...

  10. Sublime Text shift+ctrl妙用、Sublime Text快捷组合键大全

    Package Control 安装方法 首先通过快捷键 ctrl+` 或者 View > Show Console 打开控制台,然后粘贴相应的 Python 安装代码. 1 :按住shift+ ...