安吉D8-A
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A本文
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
P3509 ZAB-Frog
题目描述
在一个特别长且笔直的 Byteotian 小溪的河床上,有 块石头露出水面.它们距离小溪源头的距离分别为 .一只小青蛙正坐在其中一块石头上,准备开始它的跳跃训练.每次青蛙跳跃到距离它所在石头第 近的石头上.具体来说,如果青蛙坐在位置 的石头上,那么它将跳到这样的 上,使得:
如果 不是唯一的,那么青蛙在其中选择距离源头最近的石头.对于每一块石头分别计算,若青蛙从这块石头开始跳跃,经过 次跳跃后最终会停留在哪一块石头上?
输入格式
标准输入的第一行包含三个整数 、 和 (),用空格分隔,分别表示石头的数量、参数 和计划跳跃的次数.第二行包含 个整数 (),用空格分隔,表示小溪河床上连续石头的位置.
输出格式
你的程序应在标准输出上打印一行,包含 个整数 ,用空格分隔.数字 表示从输入顺序中的第 块石头开始跳跃 次后,青蛙最终停留的石头编号.
输入输出样例 #1
输入 #1
5 2 41 2 4 7 10输出 #1
1 1 3 1 1说明/提示
样例 #1 解释:

图中展示了青蛙从每块石头跳跃(单次跳跃)到的位置.
我们发现此题中, 的范围是 ,这显然不可能模拟跳那么多步.
通过模拟样例,我们可以发现:从一个格子开始,跳到的下一个格子是固定的.因此,考虑使用倍增.
具体来说,分别预处理出每一个点跳 1 次、跳 2 次、跳 4 次……,设点 跳了 次之后落在了 ,则转移方程为:
f[i][j] = f[i-1][f[i-1][j]];可以枚举 中所有二进制为 1 的位数 (如 的二进制表示为 ,由于二进制表示中为 1 的数位是右起第二位和右起第三位,因此此时 ),初始时,设每一个格子 上都有一只青蛙,这只青蛙实时位置为 ,对于每一个 ,将每一个格子 跳 次,即 a[i] = f[x-1][i].
最终 中记录的值就是答案了.
那么,怎么求出最初始的 ,即每一个点跳一步会落在哪里呢?
使用滑动窗口.维护一个大小始终为 的滑动窗口 .初始时,将前 个数加入滑动窗口,之后,对于每一个点 ,维护这个滑动窗口,使得其恰好包含 和离 最近的前 个数:
- 尝试将此窗口整体右移 1 格.具体的:若滑动窗口外右边第一个元素 离 的距离比滑动窗口中最左边元素 小,即第 块石头离 比第 块石头近,此时,将滑动窗口右移一格.重复这个操作,直到上述条件不满足.
- 接着,比较滑动窗口中最左端的 和最右端的 ,这两块石头是滑动窗口中离 最远的两块,比较谁离 更远,更远的一块就是 下一步会跳到的石头.
可以写出代码:
#include <bits/stdc++.h>using namespace std;const int N = 1e6 + 1;typedef long long ll;const int INF = 0x3f3f3f3f;ll n, k, m, a[N];int f[60][N];signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> k >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; } a[n + 1] = INF; // 哨兵 int l = 1, r = k + 1; for (int i = 1; i <= n; i++) { while (r < n && a[r + 1] - a[i] < a[i] - a[l]) { l++; r++; } if (a[i] - a[l] >= a[r] - a[i]) f[0][i] = l; else f[0][i] = r; }
for (int i = 1; i <= 59; i++) { for (int j = 1; j <= n; j++) { f[i][j] = f[i - 1][f[i - 1][j]]; } }
for (int i = 1; i <= n; i++) { ll x = i, st = m, wt = 0; while (st >= 1) { if (st % 2) { x = f[wt][x]; } st /= 2; wt++; } cout << x << ' '; }}如果这样写,会导致 MLE.具体来说,由于此题的 125MB 内存限制,我们只能存 个 int 类型变量.而 f 数组中有 个 int 变量.
此题应该选用滚动倍增数组进行优化.具体来说,省去 f 数组,在计算 时,动态计算当前需要的倍增数组.
标程
#include <bits/stdc++.h>#define int long longusing namespace std;const int N = 1e6 + 100;typedef long long ll;const ll INF = 2e18;int n, k;ll m, a[N];int nxt[N], cur[N], tmp[N];signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> k >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; } a[n + 1] = INF; int l = 1, r = k + 1; for (int i = 1; i <= n; i++) { while (r < n && a[r + 1] - a[i] < a[i] - a[l]) { l++; r++; } if (a[i] - a[l] >= a[r] - a[i]) nxt[i] = l; else nxt[i] = r; }
for (int i = 1; i <= n; i++) { cur[i] = i; } while (m >= 1) { if (m & 1) { for (int i = 1; i <= n; i++) { tmp[i] = cur[i]; } for (int i = 1; i <= n; i++) { cur[i] = nxt[tmp[i]]; } } m /= 2; for (int i = 1; i <= n; i++) tmp[i] = nxt[i]; for (int i = 1; i <= n; i++) nxt[i] = tmp[tmp[i]]; } for (int i = 1; i <= n; i++) { cout << cur[i] << ' '; }}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


