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

CF2165D Path Split 题解

Description

给定一个长度为 \(n\) 的整数序列 \(a_1, a_2, \ldots, a_n\)

你希望把 \(a\) 划分成若干个子序列 \(b_1, b_2, \ldots, b_k\),并满足以下条件:

  1. \(a\) 中的每个元素恰好属于某个 \(b_i\)
  2. 对于每个子序列 \(b_i\),设其元素为 \(b_{i,1}, b_{i,2}, \ldots, b_{i,p_i}\)。那么对所有 \(1 \le j < p_i\),都必须满足 \(|b_{i,j} - b_{i,j+1}| = 1\)

请计算,将 \(a\) 划分成满足条件的子序列所需的最小数量。

\(1\leq n\leq 10^6\)

Solution

首先这题是个典型的最小链覆盖的问题,考虑转成二分图最大匹配的形式:对于每个 \(i\),建立左部点 \(L_i\) 和右部点 \(R_i\)。如果 \(i<j\)\(|a_i-a_j|=1\),则让 \(L_i\)\(R_j\) 连边。答案是 \(n-最大匹配\)

直接跑匹配显然是不行的,考虑将其转化为贪心。

我们枚举左部点,考虑为每个左部点找到与其匹配的右部点。

由于左部点中值为 \(1\) 的只能匹配 \(2\),而 \(3\) 能匹配 \(2\)\(4\),所以 \(3\) 强于 \(1\),那么一定是先为 \(1\) 找到匹配,再为 \(3\) 找到匹配。

而对于左部点的 \(2\)\(4\) 情况会不太一样,因为 \(2\) 能匹配 \(1\)\(3\)\(4\) 能匹配 \(3\)\(5\),此时 \(2\) 不止能匹配 \(3\)。但是注意到如果存在一个 \(1\) 没有和 \(2\) 匹配,那么这个 \(1\) 就不能被匹配了。所以策略是对于每个 \(2\),如果能匹配 \(1\),就找到离它最近的 \(1\) 匹配,否则就找最近的 \(3\) 匹配。

最终策略就是从小到大枚举左部点的权值 \(v\),如果当前位置 \(x\) 能和值为 \(v-1\) 的右部点匹配,就找最近的 \(v-1\) 匹配,否则找最近的 \(v+1\) 匹配。

时间复杂度:\(O(n\log n)\)

Code

#include <bits/stdc++.h>// #define int int64_tconst int kMaxN = 1e6 + 5;int n;
int a[kMaxN];
std::vector<int> pos[kMaxN * 2];
std::set<int> st[kMaxN * 2];void dickdreamer() {std::cin >> n;for (int i = 1; i <= 2 * n + 1; ++i) pos[i].clear(), st[i].clear();for (int i = 1; i <= n; ++i) std::cin >> a[i], pos[a[i]].emplace_back(i), st[a[i]].emplace(i);int ans = n;for (int i = 1; i <= 2 * n; ++i) {std::reverse(pos[i].begin(), pos[i].end());for (auto x : pos[i]) {auto it = st[i - 1].lower_bound(x);if (it != st[i - 1].end()) {--ans, st[i - 1].erase(it);} else {it = st[i + 1].lower_bound(x);if (it != st[i + 1].end()) --ans, st[i + 1].erase(it);}}}std::cout << ans << '\n';
}int32_t main() {
#ifdef ORZXKRfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifstd::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);int T = 1;std::cin >> T;while (T--) dickdreamer();// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";return 0;
}
http://www.jsqmd.com/news/42977/

相关文章:

  • gdb 安装linux
  • g for linux
  • 人工智能之编程基础 Python 入门:第十章 文件读写
  • 二分图的判定
  • 连续段 DP
  • 【UE客户端/技术策划】- 工具链篇(一):输入缓冲队列 (施工中)
  • 深入探讨React源码与实现原理
  • 人工智能之编程基础 Python 入门:第九章 模块与包
  • 基于Playwright + Allure + Pytest 企业级UI录制与回放自动化测试项目
  • IM SDK选型避坑指南,2025年最新10家服务商稳定性排名
  • 自定义yml激活进本地通用yml
  • 【UE客户端/技术策划】- 工具链篇(一):通用有限分层状态机框架(浅耦合+内建+全模块化)
  • AT_jsc2019_qual_e Card Collector
  • 【UE客户端/技术策划】- 引擎扩展篇(一):移动模式拓展
  • 邻项交换
  • day26-MCP基础
  • 20232427 2025-2026-1 《网络与系统攻防技术》实验六实验报告
  • P9534 [YsOI2023] 广度优先遍历
  • 2025-11-17 ZYZ28-NOIP模拟赛-Round7 hetao1733837的record
  • day25-langgraph进阶
  • markdown格式绘制各种图
  • 11.17 考试总结
  • 计算机网络第六章---应用层(基于谢希仁老师第八版)
  • 随机化
  • 递推组合数
  • 第一次接触 JSAPIThree(百度地图 JSAPI Three)学习笔记
  • Who wants to be king:2
  • 写日记是对的
  • vulkan学习笔记第一篇_环境部署
  • 2025!超简单安装部署gitlab