1 条题解
-
0
题目理解
我们有一个环形道路, 个集章台, 种颜色各出现两次。JOI 君可以选择起点,支付费用 ,然后可以交换相邻的集章台(不能跨过起点),每次交换花费 。最后顺时针遍历所有集章台,在卡片左右框盖章,目标是获得至少 种不同的满章卡 。
关键观察
1. 满章卡数量的计算
总共有 种可能的满章卡。但实际能获得的数量取决于颜色的排列顺序。
2. 交换操作的影响
交换相邻集章台实际上是在调整颜色的相对顺序,这会影响哪些颜色对 能够被获得。
3. 问题转化
通过分析,可以发现对于每个起点 ,能够获得的满章卡数量 是确定的(与交换操作无关),而最小成本为 。
算法核心
核心思想:预处理 + 二分查找
代码通过巧妙的组合计数,预先计算每个起点对应的满章卡数量,然后通过排序和预处理来快速回答查询。
数据结构设计
- 颜色位置记录:
a[x].l和a[x].r记录颜色 的两个出现位置 - 树状数组:用于高效计算交叉关系
- 排序数组:
d[i]按满章卡数量排序,便于查询
算法流程
步骤1:预处理交叉关系
for i = 1 to 2n: if i 是颜色x的右端点: c = 在[l,r]区间内有多少左端点 s[l] += c, s[r] -= c T.update(l, 1)这里计算的是:对于每个颜色区间 ,有多少其他颜色的左端点在其中,这会影响能获得的满章卡数量。
步骤2:计算每个起点的满章卡数量
通过多个循环计算
s[i]:- 第一个循环:计算区间内的左端点
- 第二个循环:计算区间外的右端点
- 第三个循环:处理跨越起点的情况
- 第四个循环:处理其他情况
最终
s[i]表示从位置 开始能够获得的满章卡数量。步骤3:排序和预处理
d[i].s = n*n - s[i-1] // 从位置i开始能获得的满章卡数量 sort(d) // 按满章卡数量排序 // 预处理前缀最小值和后缀最小值 f[i] = min(f[i-1], d[i].c - d[i].s * k) g[i] = min(g[i+1], d[i].c)步骤4:查询处理
对于查询 :
p = lower_bound(d, x) // 找到第一个满章卡数量 ≥ x的位置 return min(f[p-1] + x*k, g[p])这里:
f[p-1] + x*k:使用交换操作来达到目标g[p]:直接选择满足条件的起点
算法正确性
1. 满章卡数量计算
通过树状数组统计颜色区间的交叉关系,准确计算了从每个起点出发能够获得的满章卡数量。
2. 成本计算
对于每个起点 :
- 如果不交换:成本为 ,获得 种卡
- 如果交换:可以通过交换调整顺序,但需要额外成本
3. 查询优化
通过排序和预处理,可以在 时间内回答每个查询。
时间复杂度
- 预处理:
- 排序:
- 查询:
总复杂度:
算法优势
- 组合计数:通过巧妙的区间统计计算满章卡数量
- 离线处理:预处理所有起点的信息
- 高效查询:通过排序和二分快速回答
- 统一处理:将交换成本统一到查询处理中
算法标签
- 组合数学
- 树状数组
- 排序
- 二分查找
- 预处理优化
- 环形结构
总结
该算法通过深入的组合分析和数据结构优化,解决了复杂的环形集章问题。核心在于准确计算每个起点对应的满章卡数量,然后通过排序和预处理实现高效查询。算法设计精妙,充分利用了问题的组合特性,是处理此类环形排列问题的优秀解法。
- 颜色位置记录:
- 1
信息
- ID
- 3893
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者