当前位置: 首页 > news >正文

题解:AT_arc218_d [ARC218D] I like Increasing

更差的阅读体验


我们称序列 \(p\) 上的一组 \((i, i+1)\) 满足 \(p_i < p_{i+1}\),为一个上升位。题目要求的就是,使上升位数量最大的前提下,最短的子序列长度。

先不考虑区间查询。首先我们要考虑的是,给你一个排列 \(a\),怎么求出它的一个子序列,使得上升位数量最大。

我们可以先得到答案上界:我们将序列 \(a\) 分成若干个极长的下降段,如 \((4, 1, 5, 3, 7, 6, 2)\) 我们分成 \((4, 1) , (5, 3), (7, 6, 2)\)。假设分成了 \(k\) 段,那么答案上界应该是 \(k-1\)

这个为什么是上界很好理解:由于每一段递减,因此每一段内部不可能产生上升位;而相邻两个段至多产生一个上升位。因此总的上升位数量不会超过段数 \(-1\)。取到这个上界的条件也就是,每段至少选 \(1\) 个,相邻段会产生上升位。

接下来考虑一个基于构造的证明,可以在证明答案能取到 \(k-1\) 的同时,求解出最短的子序列长度。

  • 对于第一段,显然选的数越小,后面的段越容易满足条件。因此我们选第一段的最小值。
  • 假设我们前一个选的数字是 \(x\),所属段是 \(i\)。那么
    • 如果 \(x>\)\(i+1\) 段的最大值,那么我们在第 \(i+1\) 段无论选什么数都无法和第 \(i\) 段产生上升位,那就取不到上界了。因此需要在第 \(i\) 段多选一个数字来满足 \(i\)\(i+1\) 段产生上升位。由于在当前段选小的数字是不劣的,我们选择当前第 \(i\) 段的最小值。容易证明当前段的最小值一定小于下一段的最大值,否则这两段应当合并称一个大段。
    • 如果 \(x<\)\(i+1\) 段的最大值,那么一定可以通过在下一段选取合适的数字满足产生上升段的条件。由于在下一段选择尽可能小的数字更容易满足以后更多段的要求,因此我们选择 \(i+1\) 段中,最小的 \(>x\) 的数字。
  • 当最后一个段有数字被选中的时候,答案被求得。

上述过程可以理解成,我们当前所选的数字在序列上跳的过程;而 \(x\) 会跳到哪里只和初始序列和 \(x\) 本身有关!

所以我们对于每个 \(x\) 预处理出 \(x\) 跳一步之后会跳到哪里。这个可以倍增优化。具体地,对于每次询问 \([l, r]\),如果 \(l, r\) 同属一段就输出 \(1\);否则就从 \(l\) 所在段最小值开始跳,直到跳到 \(r\) 所属段为止即可。

复杂度 \(O(n \log n)\)

#include<bits/stdc++.h>
#define endl '\n'
#define N 200006
using namespace std;
int n,q,c,a[N],bel[N],pos[20][N];
pair<int,int> b[N];
main()
{scanf("%d%d",&n,&q);for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1,j;i<=n;i=j+1){for(j=i;j<n&&a[j]>a[j+1];j++);b[++c]={i,j};for(int k=i;k<=j;k++)bel[k]=c;}bel[n+1]=c+1;for(int j=0;j<20;j++)for(int i=1;i<=n+1;i++)pos[j][i]=n+1;for(int i=1;i<=n;i++){if(bel[i]==c)continue;if(a[i]>a[b[bel[i]+1].first])pos[0][i]=b[bel[i]].second;else {int l=b[bel[i]+1].first;int r=b[bel[i]+1].second;while(l<=r){int mid=l+r>>1;if(a[mid]>a[i])pos[0][i]=mid,l=mid+1;else r=mid-1;}}}for(int i=1;i<20;i++)for(int j=1;j<=n;j++)pos[i][j]=pos[i-1][pos[i-1][j]];while(q--){int l,r; scanf("%d%d",&l,&r);if(bel[l]==bel[r]) {printf("1\n"); continue;}int ans=1,st=b[bel[l]].second;for(int i=19;~i;i--)if(bel[pos[i][st]]<bel[r])ans+=1<<i,st=pos[i][st];printf("%d\n",ans+1);}return 0;
}
http://www.jsqmd.com/news/753281/

相关文章:

  • 终极指南:如何使用Harepacker复活版打造专属MapleStory游戏世界 [特殊字符]
  • 如何快速上手Talking Head Anime:5分钟完成你的第一个动漫角色动画
  • Cross-Tool Skill Sync:统一管理多AI编程工具配置的工程实践
  • Codesys平台选型避坑指南:STM32/树莓派/工控机,哪种方案更适合你的项目?
  • ESP32的FATFS长文件名支持,用menuconfig勾选一下就行?聊聊堆栈选择与内存隐患
  • 别再死记硬背One-hot了!用Word2Vec实战搞定中文词向量(附Python代码)
  • 告别Rufus!用Ventoy打造你的终极系统维护U盘(支持Win11/PE/Linux)
  • 基于MCP协议集成AI助手与邮件服务:veilmail-mcp实战指南
  • 3步搞定网易云音乐NCM文件转换:ncmdumpGUI终极使用指南
  • 【微软官方未公开的5个优化技巧】:让.NET 9本地AI响应延迟从2.1s降至186ms(附Benchmark原始数据)
  • 从 CVS 到 Git:三十年源代码管理变革,Git 为何至今无可替代?
  • cState故障排除:10个常见问题及解决方案
  • 魔兽世界宏命令与API工具:从新手到高玩的终极指南
  • 异构计算环境下的推测解码优化实践
  • 如何在Keil5中配置Taotoken大模型API实现代码智能补全
  • 手把手教你用IBERT IP核测试25G光模块:从Vivado配置到XDC管脚避坑全流程
  • C# 13集合表达式配置已进入倒计时——.NET 9将废弃的旧式初始化语法,现在必须掌握的4种新范式
  • 3个技巧让AI智能体部署快如闪电:MaxKB实战指南
  • 如何评估LLM输出可靠性:LLaMA2-Accessory不确定性量化的终极指南
  • 03-Skill机制与using-superpowers
  • AI自动化图表工具PaperBanana助力科研效率提升
  • 用 AI 整理笔记,Claude 和 GPT 到底哪个更好?
  • 企业无线网络扩容实战:当核心交换机扛不住时,如何平滑迁移到AC旁挂组网架构?
  • 用Jetson Nano的串口给STM32F4‘下命令’:打造一个简单的边缘AI控制节点
  • Vital深度解析:10个必知的核心功能与使用技巧
  • Bili Music — 用 Flutter 打造一款优雅的 B 站音乐播放器手机APP
  • 从AutoDock Vina到gnina:一个药物发现工程师的实战升级笔记(附BTK抑制剂对接案例)
  • 数模竞赛避坑指南:从妈妈杯C题看新手最容易翻车的5个数据预处理和建模误区
  • 别再死磕k-ε了!Fluent里这个被低估的S-A模型,搞定壁面流动真香
  • 05-TDD系统化调试与完成前验证