最长不下降子序列(线段树优化dp)
最长不下降子序列
题目大意:
给定一个长度为 N 的整数序列:A\(_{1}\),A\(_{2}\),⋅⋅⋅,A\(_{N}\)。
现在你有一次机会,将其中连续的 K 个数修改成任意一个相同值。
请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。
最长不下降子序列是指序列中的一个子序列,子序列中的每个数不小于在它之前的数。
题目思路:
我们考虑这样两个数组,f[ ],g[ ],分别表示以i为结尾的最长不下降子序列长度和以i为开始的最长子序列长度,那我们的答案如果在不考虑修改k个数这个条件时,我们的答案就是:f[i]+g[i]-1;
当我们再去考考虑修改k个连续数这个条件时,答案就变成了:max(\(\sum_{i = n}^{i-k>=1}\)f[i-k]+k+g[i],k);
这个答案可以被看为由三部分组成:f[i-k]以i-k为结尾 + 修改后的k个数 + g[i]以i为开始
所以在实现上我们考虑用权值线段树来优化寻找最长不下降子序列这个过程
权值线段树维护的是以离散化后的点为(结束/开始)的最长不下降子序列长度
先将原数据去重+离散化
然后先处理f[i] =query(1,1,tot,1,a[i])+1 ,每次查询query(1,1,tot,1,a[i]),查询在他之前的最长子序列,之后将f[i]插入a[i]的位置
例如:a[i]离散化以后为3,那我们此时的查询即为query(1,1,tot,1,3)
这样我们就是查询从1到3的最长不下降子序列,修改也是同理,将3的值改为max(val,f[i])
之后重建线段树,倒着处理g[i]
几乎是同样的方法,只不过在这个同时处理一下ans = max(ans,f[i-k]+k+g[i]);
代码实现:
# include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
#define ls u<<1
#define rs u<<1|1
int f[N],g[N];//f[]--以i为结尾的最长...,g[]--以i为开始的最长...
int a[N];
int n,k;
struct seg{
int maxn[4*N];
void pushup(int u){
maxn[u] = max(maxn[ls],maxn[rs]);
}
void build(int u,int l,int r){
if(l == r){
maxn[u] = 0;
return;
}
int mid = l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(u);
}
void modify(int u,int l,int r,int pos,int val){
if(l == r){
maxn[u] = max(maxn[u],val);
return;
}
int mid = l+r>>1;
if(pos<=mid) modify(ls,l,mid,pos,val);
else modify(rs,mid+1,r,pos,val);
pushup(u);
}
int query(int u,int l,int r,int L,int R){
if(l==L&&r==R){
return maxn[u];
}
int mid = l+r>>1;
if(R<=mid) return query(ls,l,mid,L,R);
else if(L>mid) return query(rs,mid+1,r,L,R);
else return max(query(ls,l,mid,L,mid),query(rs,mid+1,r,mid+1,R));
}
}tr;
int b[N],tot;
int find(int u){
int l = 1,r = tot;
int ans;
while(l<r){
int mid = l+r>>1;
if(b[mid] >= u){
r = mid;
}
else l = mid+1;
}
return l;
}
int main(){
cin>>n>>k;
for(int i = 1;i <= n;++i) {
cin>>a[i];
b[i] = a[i];
}
sort(b+1,b+1+n);
tot = 1;
for(int i = 2;i <= n;++i)//离散化+去重
{
if(b[i] != b[tot]){
b[++tot] = b[i];
}
}
for(int i = 1;i <= n;++i)
{
a[i] = find(a[i]);
}
tr.build(1,1,tot);//处理f[i]
for(int i = 1;i <= n-k;++i){
f[i] = tr.query(1,1,tot,1,a[i])+1;
tr.modify(1,1,tot,a[i],f[i]);
}
tr.build(1,1,tot);//处理完f[i]后重建线段树来啊处理g[i]
int ans = 0;
for(int i = n;i-k>=1;--i){
ans = max(ans,f[i-k]+k+tr.query(1,1,tot,a[i-k],tot));
g[i] = tr.query(1,1,tot,a[i],tot)+1;
tr.modify(1,1,tot,a[i],g[i]);
}
cout<<ans<<endl;
return 0;
}
最长不下降子序列(线段树优化dp)的更多相关文章
- BZOJ2090: [Poi2010]Monotonicity 2【线段树优化DP】
BZOJ2090: [Poi2010]Monotonicity 2[线段树优化DP] Description 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k]. ...
- [AGC011F] Train Service Planning [线段树优化dp+思维]
思路 模意义 这题真tm有意思 我上下楼梯了半天做出来的qwq 首先,考虑到每K分钟有一辆车,那么可以把所有的操作都放到模$K$意义下进行 这时,我们只需要考虑两边的两辆车就好了. 定义一些称呼: 上 ...
- Codeforces Round #426 (Div. 2) D 线段树优化dp
D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...
- 【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp
题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a varian ...
- POJ 2376 Cleaning Shifts (线段树优化DP)
题目大意:给你很多条线段,开头结尾是$[l,r]$,让你覆盖整个区间$[1,T]$,求最少的线段数 题目传送门 线段树优化$DP$裸题.. 先去掉所有能被其他线段包含的线段,这种线段一定不在最优解里 ...
- 洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$
正解:线段树优化$dp$ 解题报告: 传送门$QwQ$ 难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$ 先考虑暴力$dp$?就设$f_{i,j}$表示 ...
- D - The Bakery CodeForces - 834D 线段树优化dp···
D - The Bakery CodeForces - 834D 这个题目好难啊,我理解了好久,都没有怎么理解好, 这种线段树优化dp,感觉还是很难的. 直接说思路吧,说不清楚就看代码吧. 这个题目转 ...
- 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点
容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...
- Codeforces 1603D - Artistic Partition(莫反+线段树优化 dp)
Codeforces 题面传送门 & 洛谷题面传送门 学 whk 时比较无聊开了道题做做发现是道神题( 介绍一种不太一样的做法,不观察出决策单调性也可以做. 首先一个很 trivial 的 o ...
随机推荐
- Docke 搭建 apache2 + php8 + MySQL8 环境
Docker 安装 执行 Docker 安装命令 curl -fsSL https://get.docker.com/ | sh 启动 Docker 服务 sudo service docker st ...
- Math_Music
查看代码 #REmoo的优化任务 #1.公式写在<formula_set>类中,统一管理 --- Finished 2022.8.15 12:39 #2.建立<sample_set& ...
- C++ 对于函数名的操作,函数名本身和取*以及取&的区别
void TestFunc() { } int _tmain(int argc, _TCHAR* argv[]) { cout<<TestFunc<<endl; cout< ...
- KingbaseES 的 Lateral 连接
一.什么是 Lateral 连接 根据文档,它的作用是: LATERAL 关键字可以位于子 SELECT FROM 项之前.这允许子 SELECT 引用 FROM 列表中出现在它之前的 FROM 项的 ...
- QT学习(二)
这一篇学习QT中最重要的也是最有特色的信号槽机制. (因为我学习过MFC,所以我觉得QT的信号槽机制和MFC中的消息响应机制是一一对应的.不过是MFC用的是宏来实现,而QT用的是消息和槽.相对来说QT ...
- 日志:Redo Log 和 Undo Log
本篇文章主要介绍 Redo Log 和 Undo Log: 利用 Redo Log 和 Undo Log 实现本地事务的原子性.持久性 Redo Log 的写回策略 Redo Log Buffer 的 ...
- Java中如何创建不可变(immutable)类
什么是不可变类 1. 不可变类是指类的实例一经创建完成,这个实例的内容就不会改变. 2. Java中的String和八个基本类型的包装类(Integer, Short, Byte, Long, Dou ...
- ingress-nginx自带认证功能【nginx自带】
问题:通过nginx可以给某些web网站设置登录使用的用户名和密码,现在网站部署到k8s中,通过配置nginx-ingress->service->pod来访问的,怎么给这个网站再配置上访 ...
- MySQL集群搭建(4)-MMM+LVS+Keepalived
1 LVS 介绍 1.1 简介 LVS 是 Linux Virtual Server 的简写,意即 Linux 虚拟服务器,是一个虚拟的服务器集群系统.本项目在 1998 年 5 月由章文嵩博士成立, ...
- 使用growpart扩容CentOS虚拟机磁盘
说明 一块磁盘,比如有130G,但是只有100G拿来用了,安装俩分区,一个是swap,另一个是根分区/,根分区采用的是LVM逻辑卷管理. 现在要做的是把剩余的这空闲30G分配给根分区使用 这个并不是新 ...