洛谷P3928 Sequence2(dp,线段树)
题目链接:
题目大意在描述底下有。此处不赘述。
明显是个类似于LIS的dp。
令 $dp[i][j]$ 表示:
- $j=1$ 时表示已经处理了 $i$ 个数,上一个选的数来自序列 $A[0]$ 的最长长度
- $j=2$ 表示 $A[1]$
- $j=3$ 表示 $A[2]$ 且是单调递减
- $j=4$ 表示 $A[2]$ 且是单调递增
(为了方便,我们令 $seq[x]$ 表示当上文中的 $j=x$ 时表示哪个序列)
那么有转移方程:
- $dp[i][1]=\max\limits_{1\le j<i,k\in\{1,2,3,4\}}\{dp[j][k]:A[seq[k]][j]\le A[0][i]\}+1$(从前面任选一个更小的)
- $dp[i][2]=\max\limits_{1\le j<i,k\in\{1,2,3,4\}}\{dp[j][k]:A[seq[k]][j]\ge A[0][i]\}+1$(同理)
- $dp[i][3]=\max\limits_{1\le j<i,k\in\{1,2,3\}}\{dp[j][k]:A[seq[k]][j]\le A[0][i]\}+1$(因为第三个序列连续一段状态应相同,所以不能从4转移)
- $dp[i][4]=\max\limits_{1\le j<i,k\in\{1,2,4\}}\{dp[j][k]:A[seq[k]][j]\ge A[0][i]\}+1$(同上)
可以朴素dp,复杂度 $O(n^2)$。
优化?这个真的很像LIS,那么就用二分或者树状数组/线段树吧。
二分我不会……(当然二分可能做不了)
树状数组懒得打后缀最大了……
那就线段树吧,跟普通的LIS类似,开4棵线段树,分别维护 $j=1,2,3,4$。每次求一下前缀和后缀最大,然后插进线段树中即可。
(其实是可以只开3棵线段树的,$j=1,2$ 可以合在一起。但是我从前写的时候出锅了,以为是3棵线段树的锅,改成了4棵,后来发现不是这里出bug……懒得改了)
当然 $m\le 10^9$,需要离散化。
(注:离散化后最大的数可达 $3n$,所以要开4棵大小为 $3n$ 的线段树)
时间复杂度是 $O(n\log n)$。
代码+注释:(代码长得有点丑)
- #include<bits/stdc++.h>
- using namespace std;
- const int maxn=;
- int n,a[maxn],b[maxn],c[maxn],tmp[maxn*],sz,ans=; //tmp是离散化用到的数组,sz是离散化后所有数的最大值,可以算是常数优化
- int cnt,maxv[maxn*],ch[maxn*][]; //我用的动态开点线段树,一棵树节点数为约6n,而有4棵树
- inline int lwrbnd(int x){ //离散化
- return lower_bound(tmp+,tmp+sz+,x)-tmp;
- }
- struct segment{ //线段树维护区间最大
- int n,root;
- inline void pushup(int x){
- maxv[x]=max(maxv[ch[x][]],maxv[ch[x][]]);
- }
- void build(int &x,int l,int r){
- x=++cnt;
- if(l==r) return;
- int mid=l+r>>;
- build(ch[x][],l,mid);
- build(ch[x][],mid+,r);
- }
- segment(){}
- segment(int n_):n(n_){build(root,,n);}
- void update_(int x,int l,int r,int p,int k){
- if(l==r) return void(maxv[x]=max(maxv[x],k)); //这里不能直接修改,如果之前相同位置的dp值更大则要保留
- int mid=l+r>>;
- if(mid>=p) update_(ch[x][],l,mid,p,k);
- else update_(ch[x][],mid+,r,p,k);
- pushup(x);
- }
- void update(int p,int k){
- update_(root,,n,p,k);
- }
- int query_(int x,int L,int R,int l,int r){
- if(L>=l && R<=r) return maxv[x];
- int mid=L+R>>,ans=;
- if(mid>=l) ans=max(ans,query_(ch[x][],L,mid,l,r));
- if(mid<r) ans=max(ans,query_(ch[x][],mid+,R,l,r));
- return ans;
- }
- int query(int l,int r){
- return query_(root,,n,l,r);
- }
- }sgm1,sgm2,sgm3,sgm4; //4棵线段树
- int main(){
- scanf("%d",&n);
- for(int i=;i<=n;i++) scanf("%d",a+i),tmp[i]=a[i];
- for(int i=;i<=n;i++) scanf("%d",b+i),tmp[i+n]=b[i];
- for(int i=;i<=n;i++) scanf("%d",c+i),tmp[i+*n]=c[i];
- sort(tmp+,tmp+*n+);
- sz=unique(tmp+,tmp+*n+)-tmp-;
- sgm1=segment(sz);sgm2=segment(sz);sgm3=segment(sz);sgm4=segment(sz); //建树(我的代码这里不能连等,否则4棵线段树实际的维护值一样)
- for(int i=;i<=n;i++) a[i]=lwrbnd(a[i]),b[i]=lwrbnd(b[i]),c[i]=lwrbnd(c[i]); //离散化
- for(int i=;i<=n;i++){
- int tmp1=max(sgm1.query(,a[i]),max(sgm2.query(,a[i]),max(sgm3.query(,a[i]),sgm4.query(,a[i]))))+;
- int tmp2=max(sgm1.query(b[i],sz),max(sgm2.query(b[i],sz),max(sgm3.query(b[i],sz),sgm4.query(b[i],sz))))+;
- int tmp3=max(sgm1.query(,c[i]),max(sgm2.query(,c[i]),sgm3.query(,c[i])))+;
- int tmp4=max(sgm1.query(c[i],sz),max(sgm2.query(c[i],sz),sgm4.query(c[i],sz)))+;
- //上面四行是从线段树中查询前缀/后缀最大值并且更新
- ans=max(ans,max(tmp1,max(tmp2,max(tmp3,tmp4))));
- sgm1.update(a[i],tmp1);sgm2.update(b[i],tmp2);sgm3.update(c[i],tmp3);sgm4.update(c[i],tmp4); //插入线段树
- }
- printf("%d\n",ans);
- }
线段树优化dp
洛谷P3928 Sequence2(dp,线段树)的更多相关文章
- 【BZOJ】1012: [JSOI2008]最大数maxnumber /【洛谷】1198(线段树)
Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插 ...
- 洛谷题解P4314CPU监控--线段树
题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...
- 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)
洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...
- 洛谷P4428二进制 [BJOI2018] 线段树
正解:线段树 解题报告: 传送门! 话说开始看到这题的时候我想得hin简单 因为关于%3有个性质就是说一个数的各个位数之和%3=这个数%3嘛,小学基础知识? 我就想着,就直接建一棵树,只是这棵树要用个 ...
- 洛谷P4065 [JXOI2017]颜色(线段树)
题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...
- 洛谷P5111 zhtobu3232的线段树
题意:给定线段树,上面若干个节点坏了,求能表示出多少区间. 区间能被表示出当且仅当拆出来的log个节点都是好的. 解:每个区间在最浅的节点处计算答案. 对于每个节点维护从左边过来能有多少区间,从右边过 ...
- 洛谷P3960 列队 NOIp2017 线段树/树状数组/splay
正解:动态开点线段树 解题报告: 传送门! 因为最近学主席树的时候顺便get到了动态开点线段树?刚好想起来很久很久以前就想做结果一直麻油做的这题,,,所以就做下好了QAQ 然后说下,这题有很多种方法, ...
- 题解——洛谷P2781 传教(线段树)
可以说是数据结构学傻了的典型案例了 昨天跳到这题上 然后思考了一下 噫!好!线段树裸题 然后打完板子,发现\( n \le 10^9 \) 显然线段树直接做不太行 然后这题又只有普及的难度 然后我就 ...
- 洛谷P4198 楼房重建(线段树)
题意 题目链接 Sol 别问我为什么发两遍 就是为了骗访问量 这个题的线段树做法,,妙的很 首先一个显然的结论:位置\(i\)能被看到当且仅当\(\frac{H_k}{k} < \frac{H_ ...
随机推荐
- 20155238 2016-2017-2 《JAVA程序设计》第十周学习总结
教材学习内容总结 # Java计算机网络基础 计算机网络 计算机网络是通过传输介质.通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共享和数据传输的系统.网络编程就就是编写程序使 ...
- Python基础(字符串和编码)
字符编码 我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特 ...
- python 回溯法 子集树模板 系列 —— 18、马踏棋盘
问题 将马放到国际象棋的8*8棋盘board上的某个方格中,马按走棋规则进行移动,走遍棋盘上的64个方格,要求每个方格进入且只进入一次,找出一种可行的方案. 分析 说明:这个图是5*5的棋盘. 图片来 ...
- Kubernetes学习之路(二十三)之资源指标和集群监控
目录 1.资源指标和资源监控 2.Weave Scope监控集群 (1)Weave Scope部署 (2)使用 Scope (3)拓扑结构 (4)实时资源监控 (5)在线操作 (6)强大的搜索功能 2 ...
- CTE 递归查询全解
TSQL脚本能实现递归查询,用户使用共用表表达式 CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询.本文详细介绍CTE递归调用的特性和使用示例,递归查询 ...
- jQuery .attr() vs. .prop()
Property vs. Attribute 在开始正式比较prop()和attr()两个jQuery方法之前,我们有必要先弄清一下Property和Attribute两个单词的意思.在中文里面,它们 ...
- 巧用cheerio重构grunt-inline
grunt-inline是楼主之前写的一个插件,主要作用是把页面带了__inline标记的资源内嵌到html页面去.比如下面的这个script标签. <script src="main ...
- 501. Find Mode in Binary Search Tree【LeetCode by java】
Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred ...
- 毕业设计---json,Struts,ajax以及JQuery简单案例
<!-- Struts2的xml文件配置 --><struts> <package name="default" namespace="/& ...
- Struts2中 radio标签的详细使用方法
首先在页面中引入struts标签库: <%@ taglib prefix="s" uri="/struts-tags"%> 在JSP页面中创建单选按 ...