专栏文章
题解:P10403 「XSOI-R1」跳跃游戏
P10403题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @minwdimu
- 此快照首次捕获于
- 2025/12/02 09:26 3 个月前
- 此快照最后确认于
- 2025/12/02 09:26 3 个月前
前言
这是我第一次写题解,因为其他大佬的题解实在是没看懂,所以自己写一篇讲一下我的思路。
思路
不难发现, 有一个性质:如果 的 为 ,那么任何 的 。
也就是说, 随区间右端点增加不会突然变小后再变大 → 单调性→二分查找可行。
也就是说, 随区间右端点增加不会突然变小后再变大 → 单调性→二分查找可行。
- 用 表预处理区间 ,实现 查询;
- 对每个左端点 ,通过二分找到所有 不变的连续区间;
- 针对 为 或 的区间,分别计算符合奇偶条件的长度总和;
- 累加所有贡献得到答案;
对于通过二分找到所有 不变的连续区间 ,
我们可以分段二分,二分查找分段区间 不变的右端点 ,如下图所示:

一次二分确定一段 ,这段区间 都是相同的,然后更新 ,继续找下一段。
AC代码
CPP#include <bits/stdc++.h>
using namespace std;
int n, a[600086], st[600086][20], lg[600086];
long long ans;
// 预处理st表
void build() {
// 预处理log2
for (int i = 2; i <= n; i++)
lg[i] = lg[i >> 1] + 1;
// st表初始化
for (int i = 1; i <= n; i++)
st[i][0] = a[i];
// 递推ST表
for (int j = 1; j <= lg[n]; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = __gcd(st[i + (1 << (j - 1))][j - 1], st[i][j - 1]);
}
}
// 区间gcd的O(1)查询
int get_gcd(int l, int r) {
int j = lg[r - l + 1];
return __gcd(st[l][j], st[r - (1 << j) + 1][j]);
}
// 计算区间[l, r]内所有偶数的和
long long ou_sum(int l, int r) {
if (l > r)
return 0;
int first;
if (l % 2 == 0) // 起点是偶数
first = l;
else
first = l + 1;
if (first > r)
return 0;
int back;
if (r % 2 == 0) // 终点是偶数
back = r;
else
back = r - 1;
int count = (back - first) / 2 + 1; // 项数
return (long long)(first + back) * count / 2; // 高斯求和
}
// 计算区间[l, r]内所有奇数的和
long long ji_sum(int l, int r) {
if (l > r)
return 0;
int first;
if (l % 2 == 1) // 起点是奇数
first = l;
else
first = l + 1;
if (first > r)
return 0;
int back;
if (r % 2 == 1) // 终点是奇数
back = r;
else
back = r - 1;
int count = (back - first) / 2 + 1;
return (long long)(first + back) * count / 2; // 同上
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
build();
// 枚举左端点x
for (int x = 1; x <= n; x++) {
int goal_gcd = a[x], pos = x; // 当前区间的目标gcd(初始化为第一个数)和起始位置
while (pos <= n) {
int l = pos, r = n, y;
// 二分查找:找到[x,y]的gcd等于goal_gcd的最远右端点y
while (l <= r) {
int mid = (l + r) / 2;
if (get_gcd(x, mid) == goal_gcd) {
y = mid; // 记录下来
l = mid + 1; // 还能远
} else
r = mid - 1; // 太远了,往回走
}
// 根据gcd的值,累加对应区间贡献
if (goal_gcd == 2) {
// 分段区间gcd等于2,统计[x,y]的偶数和
ans += ou_sum(pos - x + 1, y - x + 1);
} else if (goal_gcd == 3) {
// 分段区间gcd等于3,统计[x,y]的奇数和
ans += ji_sum(pos - x + 1, y - x + 1);
}
// 如果已经到头,就结束
if (y == n)
break;
// 否则,更新goal_gcd,并继续处理下一个分段区间
goal_gcd = __gcd(goal_gcd, a[y + 1]);
pos = y + 1;
}
}
cout << ans;
return 0;
}
因为 每次只能下降,最多下降 次,所以时间复杂度:。
另外 表的预处理复杂度是 ,不会成为瓶颈。
另外 表的预处理复杂度是 ,不会成为瓶颈。
如果有错误的话,感谢大家指正。
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...