1 条题解
-
0
题目理解
我们有 个选手排成一列,旗手在最前面。每个选手 有一个参数 控制其移动规则:
- 如果与前面的人距离 ,则不动
- 如果距离 ,则立即前进到距前者 单位的位置
我们需要回答 个查询:在时刻 ,位置在 区间内的选手数量。
问题分析
1. 移动规律分析
通过观察可以发现,选手的移动具有周期性和分组性:
- 选手们会形成若干组,组内选手保持相对位置不变
- 每组选手的移动模式相同,具有周期性
- 每个组在移动时作为一个整体前进
2. 关键观察
选手 的移动模式取决于前面选手的移动模式。具体来说:
- 选手们会形成连续的块(block),块内选手共享相同的移动周期
- 每个块有固定的周期 和每周期前进距离
算法思路
1. 预处理块信息
代码的核心是预处理出选手的分块信息:
vector<long long> p(n + 1, inf), w(n + 1, 0); p[0] = 1; // 旗手的周期 w[0] = 1; // 旗手每周期前进距离 for (int i = 1; i <= n; i++) { p[i] = (d[i] + w[i - 1] - 1) / w[i - 1] * p[i - 1]; w[i] = p[i] / p[i - 1] * w[i - 1]; if (p[i] > inf) break; }这里:
p[i]:选手 所在块的移动周期w[i]:选手 所在块每周期前进的距离
2. 分块处理
将具有相同周期的连续选手划分为一个块:
vector<pair<int, int>> block; for (int l = 0, r = 0; l <= n; l = r + 1) { r = l; while (r <= n && p[l] == p[r]) r++; r--; block.push_back({l, r}); }3. 查询处理
对于每个查询,计算每个块在时刻 时的位置范围:
for (auto [L, R] : block) { long long per = p[L], walk = w[L]; int go = t / per * walk; // 总前进距离 int cl = l - go, cr = r - go; // 相对位置 // 计算在相对位置[cl, cr]内的选手数量 ans += max(0, min(cr, -L) - max(cl, -R) + 1); }关键算法细节
1. 周期计算原理
对于选手 :
- 如果 ,则与前面选手保持相同周期
- 如果 ,则形成新的周期:
p[i] = ceil(D_i / w[i-1]) * p[i-1] w[i] = p[i] / p[i-1] * w[i-1]
2. 位置计算
在时刻 ,一个周期为 、每周期前进 的块的总前进距离为:
前进距离 = (T / p) * w选手 的绝对位置为:
位置 = 前进距离 - i3. 区间计数
对于块 ,在相对坐标系中:
- 选手 的相对位置是
- 查询区间 映射到相对坐标系为
- 计算相对位置在 内的选手数量
复杂度分析
- 预处理:
- 每个查询:,块数最多为
- 总复杂度:
对于 ,这是可接受的。
算法优化
1. 提前终止
当周期超过数据范围时提前终止:
if (p[i] > inf) break;2. 整数运算
所有计算使用整数运算,避免浮点数误差。
算法标签
- 数学:周期性分析、整数运算
- 分块思想:将选手按移动模式分组
- 预处理:预先计算所有块的周期信息
- 区间计数:高效回答区间查询
总结
这道题通过深入分析选手移动的周期性规律,将问题转化为分块处理:
- 规律发现:识别出选手移动的周期性和分组性
- 数学建模:用周期 和前进距离 描述每个块的移动
- 高效查询:利用分块信息快速回答位置查询
- 边界处理:正确处理各种边界情况
该解法充分利用了问题的特殊性质,通过数学分析和分块技术实现了高效查询,是数学思维与算法设计的完美结合。
- 1
信息
- ID
- 5321
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者