【板子】短序列dp(换成维护更小常数维度的dp)
当dp的转移常数空间非常大比如dp[100000][100000]但是dp内维护的值非常小比如1-10我们就可以选择维护dp[100000][10]或dp[10][100000];而值代表另一个维度比如 l r的中间翻倍子序列长度翻倍子序列很小所以可以维护从任意 r 结尾子序列长度为 x 的最小 l 来极大缩小常数。DP 状态的某一维通常是长度很小≤60但每一层的转移涉及在满足某个权值条件的历史状态中找最优值需要用线段树/树状数组加速。一、方法总结核心思想步骤做什么为什么① 发现长度上界​证明/观察到答案枚举长度 ≤ O(logV)可以逐长度枚举不会 TLE② 定义 DP 状态​fi,j​ 以 i 结尾、长度为 j 的某种极值通常是最小起点 / 最大终点把选子序列转化为递推③ 值域限制 → 区间查询​转移条件形如 L≤ak​≤R转化为在离散化权值上的区间查询线段树/树状数组直接上④ 控制下标顺序​正序/倒序枚举 i保证查到的都是前面/后面的状态不用再管下标维线段树只管权值⑤ 离线/半在线回答询问​每算完一层顺手用后缀/前缀最值更新所有询问的答案免去存所有 fi,j​适用场景速判看到以下特征就可以往这个方向想子序列需要满足某种乘法/指数型增长条件2x≤y≤3x、y≥kx、yy×c 等//dp值限制在非常小的范围多次区间询问求最长合法子序列长度转移条件是前面找一个满足条件的位置取某个最值常见变体变体改哪里条件是 y≥2x只有下界rightId设为 m无穷大条件是 y2x 精确相等区间退化为单点查询用数组/map 就行求的是最长长度的具体方案多记一个pre指针回溯询问强制在线把所有 fi,j​ 存下来对每列做 RMQ/线段树长度不是 log 级而是 O(n)失效得换思路单调栈/贪心/决策单调性二、通用板子定义序列 b (b1, b2, . . . , bm) 是合法的当且仅当对于所有 2 ≤ i ≤ m都有2bi−1 ≤ bi ≤ 3bi−1.特别地长度为 1 的序列一定合法。给定一个长度为 n 的正整数序列 a需要回答 q 个区间询问。每次给定区间 [l, r]求 al, al1, . . . , ar 中最长合法子序列的长度。所选元素不要求连续但必须保持相对顺序。1 ≤ n, q ≤ 2 · 10e51 ≤ ai ≤ 10e181 ≤ l ≤ r ≤ n。核心思路1. DP 状态设计设 fi,j​ 表示以第 i 个元素结尾、长度为 j 的合法子序列中最靠左的开头位置即最小的起始下标。当 j1 时fi,1​i单个元素自己就是合法子序列。当 j1 时我们要找一个 ki满足 2ak​≤ai​≤3ak​且 fk,j−1​ 尽可能小这样得到的序列整体更靠右更容易被包含在询问区间里。转移方程fi,j​ki2ak​≤ai​≤3ak​​min​fk,j−1​2. 用线段树加速转移转移有两个限制下标限制ki我们只需从小到大或从大到小枚举 i保证只用到之前的 k。权值限制ak​∈[⌈ai​/3⌉,⌊ai​/2⌋]等价于 ai​∈[2ak​,3ak​]。代码采用了倒序枚举 i从 n 到 1的巧妙方式维护一棵线段树以离散化后的 a 值为下标线段树每个节点存储已经遍历过的元素下标更大的 fk,j−1​ 的最小值。对于当前 i需要找的是后面的某个 kki满足 2ai​≤ak​≤3ai​。这正好对应线段树上区间 [leftIdi​,rightIdi​] 的查询。查询得到的最小值就是 fi,j​代码中记为cur[i]。然后把当前的 fi,j−1​即prev[i]更新进线段树供更前面的 i 使用。这样我们在 O(logm)m 为离散化后数值种类时间内完成了单次转移。3. 如何回答区间询问对于一个询问 [l,r]如果存在某个 i∈[l,r] 使得 fi,j​≥l那就说明能在 [l,r] 内找到一个长度为 j 的合法子序列开头 ≥l结尾 i≤r。代码中使用了一个等价但更易实现的判断计算出cur[i]即 fi,j​后求一个后缀最小值数组suf[i] min_{k \ge i} cur[k]。如果suf[l] r说明存在一个结尾 ≥l 且开头 ≤r 的长度为 j 的序列结合 f 的定义这个序列整体落在 [l,r] 内的条件被满足于是更新答案为 j。由于我们是按长度 j2,3,… 依次计算的并且只要当前长度可行就更新ans[id] len最终每个询问就会留下最大可行长度。核心结构//涉及权值线段树离散化不是最基础的板子仅面向类似题目// // 通用板子逐层DP 权值线段树优化转移 // 解决区间询问最长合法子序列长度 // 时间复杂度O(n log n * max_len) // #include bits/stdc.h using namespace std; const int INF 1e9; const int MAXN 200005; // ---------- 权值线段树区间最小值---------- struct SegMin { int n; vectorint t; SegMin(int m) { n 1; while (n m) n 1; t.assign(2 * n, INF); } void update(int p, int val) { if (val INF) return; int x p n - 1; if (t[x] val) return; t[x] val; for (x 1; x; x 1) t[x] min(t[x1], t[x1|1]); } int query(int l, int r) { if (l r) return INF; int res INF; l n - 1; r n - 1; while (l r) { if (l 1) res min(res, t[l]); if (!(r 1)) res min(res, t[r--]); l 1; r 1; } return res; } }; // ---------- 主逻辑 ---------- void solve(const vectorlong long a, const vectorpairint,int queries) { int n a.size() - 1; // a[1..n] int q queries.size(); // --- Step 1: 离散化 --- vectorlong long vals(a.begin() 1, a.end()); sort(vals.begin(), vals.end()); vals.erase(unique(vals.begin(), vals.end()), vals.end()); int m vals.size(); auto get_id [](long long x) { return lower_bound(vals.begin(), vals.end(), x) - vals.begin() 1; }; auto get_end [](long long x) { // upper_bound 版本 return upper_bound(vals.begin(), vals.end(), x) - vals.begin(); }; vectorint pos(n 1), L(n 1), R(n 1); for (int i 1; i n; i) { pos[i] get_id(a[i]); L[i] get_id(a[i] * 2); // ← 题目条件2*a[i] ≤ a[k] R[i] get_end(a[i] * 3 - 1); // ← 题目条件a[k] ≤ 3*a[i] } // --- Step 2: 初始化长度为1 --- vectorint prev(n 2); for (int i 1; i n; i) prev[i] i; // --- Step 3: 逐层 DP --- vectorint cur(n 2); vectorint suf(n 2); vectorint ans(q, 1); SegMin seg(m); for (int len 2; ; len) { seg SegMin(m); // 重置 fill(cur.begin(), cur.end(), INF); // 倒序枚举找后面的 k i for (int i n; i 1; --i) { if (L[i] R[i]) cur[i] seg.query(L[i], R[i]); seg.update(pos[i], prev[i]); } // 后缀最小值 int best INF; for (int i n; i 1; --i) { best min(best, cur[i]); suf[i] best; } if (best INF) break; // 这一层已经没有合法序列了 // 更新所有询问 for (int id 0; id q; id) { int l queries[id].first, r queries[id].second; if (suf[l] r) ans[id] len; } prev.swap(cur); } // 输出 for (int x : ans) cout x \n; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, q; cin n q; vectorlong long a(n 1); for (int i 1; i n; i) cin a[i]; vectorpairint,int queries(q); for (int i 0; i q; i) cin queries[i].first queries[i].second; solve(a, queries); return 0; }权值线段树SegMinstruct SegMin { int n; vectorint t; SegMin(int m) { n 1; while (n m) n 1; t.assign(2 * n, INF); } void update(int p, int val) { ... } int query(int l, int r) { ... } };普通线段树权值线段树下标含义​数组的位置第几个元素元素的值本身​叶子节点​对应a[i]这个位置的数对应值为x的数有多少个典型用途​区间求和、区间修改、区间最值查排名、查第 k 大、统计某个值范围内有多少个数这是一个维护区间最小值的线段树但它的下标不是数组的位置而是数值离散化后的值。比如原数组里有数字{3, 5, 12, 100}离散化后变成{1, 2, 3, 4}。那线段树下标1~4分别对应这些数。在每个下标里我们存的是已经遍历过的、值为该数的那些元素它们的 DP 值的最小值。两个操作update(p, val)告诉线段树值为p的位置我来报到一个值val你帮我取个 min。query(l, r)问线段树在区间[l, r]也就是值在[l, r]范围内的所有元素里最小的 DP 值是多少为什么用线段树因为我们要快速在一段权值区间里找最小值暴力扫是 O(n) 的线段树是 O(logn)。离散化部分vectorlong long vals(a.begin() 1, a.end()); sort(vals.begin(), vals.end()); vals.erase(unique(vals.begin(), vals.end()), vals.end()); int m vals.size();原数组 ai​ 的范围是 1∼10e18不能直接当下标用。离散化就是把它们压缩到 1∼mm≤n让线段树开得下。auto get_id [](long long x) { return lower_bound(vals.begin(), vals.end(), x) - vals.begin() 1; };二分查找给我一个数 x告诉我它在离散化数组里的编号从 1 开始方便线段树。pos[i] get_id(a[i]); // a[i] 自己的编号 L[i] get_id(a[i] * 2); // 2*a[i] 的下界编号 R[i] get_end(a[i] * 3 - 1); // 3*a[i] 的上界编号回忆题目要求2ak​≤ai​≤3ak​如果我们正序 DP 找前面的 k。但我们的板子是倒序做的含义反过来找后面的 k满足 2ai​≤ak​≤3ai​。L[i]后面那个数 ak​ 至少要是 2ai​所以在离散化数组里的最小位置。R[i]后面那个数 ak​ 最多是 3ai​所以在离散化数组里的最大位置。也就是说后面所有合法的 k它们的pos[k]一定落在[L[i], R[i]]这个闭区间里。改成别的题时需要动的地方位置改什么L[i] get_id(a[i] * 2)改成下界条件R[i] get_end(a[i] * 3 - 1)改成上界条件seg.query(L[i], R[i])如果要求最大值就换成SegMax相应改min→max、INF→-INFprev[i] i如果 DP 定义变了比如以 i 开头的初始化跟着改suf[l] r判断条件根据你定义的 f 的含义调整

相关新闻

软考高项论文写作全攻略:从理论到实战的45分通关秘籍

软考高项论文写作全攻略:从理论到实战的45分通关秘籍

1. 项目概述:为什么说论文是软考高项的“生死线”?如果你正在备考信息系统项目管理师(俗称“软考高项”),那你一定听过这句话:“得论文者得天下”。这绝不是危言耸听。作为高级资格认证,高项考试…

2026/10/15 18:11:34 阅读更多 →
高速光耦HCPL2630数据手册解读与电路设计实战指南

高速光耦HCPL2630数据手册解读与电路设计实战指南

1. 项目概述:从“芯片恐惧症”到“手册自由”每次拿到一片新的芯片,尤其是像光耦这种看起来“简单”但参数表密密麻麻的器件,你是不是也和我一样,有过瞬间的迷茫?数据手册动辄十几二十页,全英文&#xff0c…

2026/10/13 22:33:22 阅读更多 →
小红书内容管理神器:XHS-Downloader 的完整使用指南

小红书内容管理神器:XHS-Downloader 的完整使用指南

小红书内容管理神器:XHS-Downloader 的完整使用指南 【免费下载链接】XHS-Downloader 小红书(XiaoHongShu、RedNote)链接提取/作品采集工具:提取账号发布、收藏、点赞、专辑作品链接;提取搜索结果作品、用户链接&#…

2026/10/13 8:56:00 阅读更多 →

最新新闻

IPMItool源码解析:从接口抽象到BMC诊断命令扩展

IPMItool源码解析:从接口抽象到BMC诊断命令扩展

简介:IPMItool 源码包是一份面向服务器运维工程师、系统管理员及BMC嵌入式开发者的开源工具源码,用于通过IPMI协议对BMC进行远程监控、传感器数据读取与电源管理。资源共171个文件,压缩包仅752KB,以50个头文件和49个C源文件为核心…

2026/10/15 18:11:23 阅读更多 →
用 Bonsai 打造游戏 NPC AI:从巡逻、追击到攻击的完整行为树实战教程

用 Bonsai 打造游戏 NPC AI:从巡逻、追击到攻击的完整行为树实战教程

【免费下载链接】bonsai Rust implementation of behavior trees for deterministic AI (now with Python bindings) 项目地址: https://gitcode.com/gh_mirrors/bonsai1/bonsai 点击查看 免费下载 Bonsai(crate 名 bonsai-bt)是一个用 Rust…

2026/10/15 18:11:23 阅读更多 →
VS2017编译64位libssh2库:OpenSSL与CMake避坑指南

VS2017编译64位libssh2库:OpenSSL与CMake避坑指南

简介:面向Windows 64位开发需求的libssh2预编译资源包,解决了在VS2017环境中自行配置CMake、OpenSSL依赖并手动编译的繁琐问题。包内已生成可直接链接的64位libssh2静态库,配套完整的头文件集合及OpenSSL相关声明,并附上两个64位运…

2026/10/15 18:11:23 阅读更多 →
数据链路层协议原理与实战:以太网帧、VLAN、STP排障全解析

数据链路层协议原理与实战:以太网帧、VLAN、STP排障全解析

做网络排障这么多年,我有个根深蒂固的习惯:不管上层协议多复杂,只要链路不通,第一件事永远是打开抓包软件看二层帧。原因很简单,IP地址再会规划,路由协议再聪明,最终都要靠数据链路层把帧从网线…

2026/10/15 18:11:23 阅读更多 →
HTML进阶实战:从语义化结构到原生交互与性能优化

HTML进阶实战:从语义化结构到原生交互与性能优化

聊到HTML进阶,很多人第一反应是去学一堆新标签、背属性列表,或者找几套酷炫的模板抄一抄。但在实际项目里摸爬滚打几年之后,我发现真正的进阶,是从"能写页面"升级到"能把页面写明白"——让结构自己会说话&…

2026/10/15 18:11:23 阅读更多 →
MFC Tab Control 标签切换实战:子页面管理与消息响应全解析

MFC Tab Control 标签切换实战:子页面管理与消息响应全解析

简介:一套基于MFC对话框的TabControl标签切换示例工程,面向需要实现多页面动态切换的Windows桌面应用开发者。压缩包共39个文件,包含7个C头文件、5个源程序文件,以及Visual Studio工程文件、资源脚本、可执行程序与调试符号等&…

2026/10/15 18:10:22 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →