1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:45:27,当前版本为作者最后更新于2023-03-07 14:47:20,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
通过样例提示或手玩几组小数据,我们发现有些选手一定的轮数后不再移动,而其它选手按从 到 的顺序环状移动。
- 考虑选手 ,他最终会在靶 停下来。
- 考虑选手 ,他最终会在某个非靶 的位置停下来,因为他无法打败任何人,所以一旦进入某个非靶 的位置,他就永远无法离开了。
- 考虑选手 ,他最终会在某个不是选手 或选手 停下来的位置停下来,原因同理。
以此类推,可知不移动的选手为 和 ,而环状移动的选手为 。我们将 个靶位按编号排成圆圈。
我们探究什么时候所有选手会有规律地移动。一个直观猜测是,等选手 进入靶位 后,选手 会在不超过 轮后停在靶位 。因此,至多 轮后整个局面会以 为循环节循环。
轮的上界已经够用了,而且很好证明。作为补充解释,我们尝试证明一个更强的结论:
会在不超过 轮后停在靶位 。
证明:考虑某个靶位 ()且 轮后没有 ,那么 从来没有被这样的选手经过,因为一旦经过就一直存在,无论留下的选手编号是什么。
在第一轮时, 左侧的靶位(靶位 )至多剩下一名 。
在第二轮时, 左侧的两个靶位总共至多剩下两名 。
特别地,在第 轮时,靶位 至多剩下 名 ,且如果剩下 名,则靶位 上至少有一名,此时我们再等一轮,第 轮时,靶位 至多剩下 名 。
以此类推,可知第 轮时, 左侧的 个靶位(也就是除了靶位 本身的其它所有靶位)至多剩下 名 ,但总共有 名 ,这与靶位 上没有 矛盾了。
这样,我们将 替换为最小的 (或 )且 ,答案不会改变。这给予我们 的模拟做法。
考虑优化单次 的模拟。我们观察到,若主角的技术水平为 ,则在模拟的过程中,所有 的选手是等价的,称为 类选手;所有 的选手是等价的,称为 类选手。并且,根据移动规则,类似上述证明,若有 类选手进入靶位 ,则该靶位一直有 类选手;若有 类选手进入靶位 ,则该靶位一直有 类选手。
因此,只要我们能够对于靶位 求出第一次有 类选手的时间 ,对于靶位 求出第一次有 类选手的时间 ,即可在任意时刻确定每个靶位上选手的类型,从而只维护主角的移动而不需要维护其它选手的移动。
显然容易,考虑 。设 表示 类选手数量关于靶位的前缀和。我们认为,如果两个 类选手相遇,那么 初始位置靠后的选手获胜。这样,考虑最终移动到靶位 上的选手初始靶位编号 :
- 如果 到 不经过靶位 ,则 移动到 的充要条件是 至少有 个 类选手,即 ,且 恰好在第 轮到达靶位 ,每一步都向前移动 。又因为每个靶位上至多有两个 类选手,所以符合条件的最小的 一定满足 或 。实际上,特判掉靶位 本来就有 类选手的情况之后, 只能等于 。
- 如果 到 经过靶位 ,那么稍微复杂一些。首先, 移动到 的充要条件是 至少有 个 类选手,即 。类似地,除了靶位 没有 类选手,靶位 有两个 类选手的情况, 只能等于 。但是, 并不一定恰好在第 轮到达靶位 :如果 在到达靶位 时,靶位 有两个选手,那么 会在这里停顿一下,使得 在第 轮到达靶位 。易知其充要条件为靶位 初始全部有两个 类选手。
求出 之后,单次模拟的时间复杂度就可以做到 。
因为超过一个 类选手只能打败他,所以在靶位 上,相较于靠前的起始位置,靠后的起始位置总要打败更多的 类选手。而在靶位 上,两个不同的起始位置可能等到相同的轮数才回到 ,但靠后的起始位置一定不会在靠前起始位置之前(更少的轮数)回到 。因此,如果我们在线性的尺度下观察整个过程,设 表示初始位置 在线性尺度下的最终位置,即 减去主角从 出发经过 轮的移动次数,那么对于 ,。
因为 ,所以 。我们枚举 的所有 的倍数 ,总共 个,二分求出第一个 使得 ,则初始位置 以及对应的最终位置才有可能更新答案。时间复杂度 。
#include <bits/stdc++.h> using namespace std; using ll = long long; bool Mbe; constexpr int N = 4e5 + 5; int n, R, a[N], b[N]; pair<int, int> ans = {N, 0}; auto simulate(int p) { for(int i = 1; i < 2 * p; i++) a[i] = b[i + 1]; a[2 * p] = b[1]; for(int i = 2 * p + 1; i <= 2 * n; i++) a[i] = b[i]; static int t[N], s[N], buc[N << 1]; memset(s, 0, N << 2), memset(buc, 0x3f, N << 3); for(int i = 1; i <= 2 * n; i++) if(a[i] == 2) s[i + 1 >> 1]++; for(int i = 1; i <= n; i++) s[i + n] = s[i], t[i] = 1e9; for(int i = 1; i <= 2 * n; i++) s[i] += s[i - 1]; for(int i = 0; i <= 2 * n; i++) s[i] += N - i; for(int i = n + 1; i > 1; i--) { // i 从 n + 1 开始,特判特殊情况 if(s[i] >= s[i - 1]) t[i] = 1; else t[i] = buc[s[i - 1]] - i + 1; buc[s[i]] = i; } memset(buc, 0x3f, N << 3); for(int i = 2 * n; i > 1; i--) { if(i > n) buc[s[i]] = i; else { int p = buc[s[i - 1] - 1]; if(p < N) t[i] = min(t[i], p - i + (s[p] - s[n] == p - n ? 2 : 1)); } } int fir = 0, cur = 0, fin; for(int i = 1; i <= 2 * n; i++) { if(a[i] == 0 && !fir) fir = i + 1 >> 1; else if(a[i] == 1) fin = cur = i + 1 >> 1; } for(int _ = 1; _ <= R; _++) { if(cur == 1) {if(fir <= _) cur = n, fin--;} else if(t[cur] <= _) cur--, fin--; } ans = min(ans, {cur, -p}); return make_pair(cur, fin); } bool Med; int main() { fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0); #ifdef ALEX_WEI FILE* IN = freopen("1.in", "r", stdin); FILE* OUT = freopen("1.out", "w", stdout); #endif ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n >> R, R = n + R % n; for(int i = 1; i <= 2 * n; i++) cin >> b[i]; if(b[1] == 1) cout << n << "\n", exit(0); for(int i = 2; i <= 2 * n; i++) b[i] = b[i] < b[1] ? 0 : 2; b[1] = 1; auto fst = simulate(1), lst = simulate(n); for(int i = fst.second; i < lst.second; i++) { if(i != fst.second && (i % n + n) % n != 1) continue; int l = 1, r = n; while(l < r) { int m = l + r >> 1; auto dat = simulate(m); if(dat.second >= i) r = m; else l = m + 1; } int v = simulate(l).second; r = n; while(l < r) { int m = l + r + 2 >> 1; auto dat = simulate(m); if(dat.second <= v) l = m; else r = m - 1; } simulate(l); } cout << -ans.second << "\n"; cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n"; return 0; }
- 1
信息
- ID
- 8440
- 时间
- 2000ms
- 内存
- 64MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者