1 条题解

  • 0
    @ 2025-8-24 22:45:27

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Alex_Wei
    **

    搬运于2025-08-24 22:45:27,当前版本为作者最后更新于2023-03-07 14:47:20,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P9112 [IOI2009] Archery

    通过样例提示或手玩几组小数据,我们发现有些选手一定的轮数后不再移动,而其它选手按从 nn11 的顺序环状移动。

    • 考虑选手 11,他最终会在靶 11 停下来。
    • 考虑选手 2n2n,他最终会在某个非靶 11 的位置停下来,因为他无法打败任何人,所以一旦进入某个非靶 11 的位置,他就永远无法离开了。
    • 考虑选手 2n12n - 1,他最终会在某个不是选手 11 或选手 2n2n 停下来的位置停下来,原因同理。

    以此类推,可知不移动的选手为 11n+22nn + 2\sim 2n,而环状移动的选手为 2n+12\sim n + 1我们将 nn 个靶位按编号排成圆圈

    我们探究什么时候所有选手会有规律地移动。一个直观猜测是,等选手 11 进入靶位 11 后,选手 n+22nn + 2\sim 2n 会在不超过 nn 轮后停在靶位 2n2\sim n。因此,至多 2n2n 轮后整个局面会以 nn 为循环节循环。

    2n2n 轮的上界已经够用了,而且很好证明。作为补充解释,我们尝试证明一个更强的结论:

    n+22nn + 2\sim 2n 会在不超过 nn 轮后停在靶位 2n2\sim n

    证明:考虑某个靶位 ii2in2\leq i\leq n)且 nn 轮后没有 n+22nn + 2\sim 2n,那么 ii 从来没有被这样的选手经过,因为一旦经过就一直存在,无论留下的选手编号是什么。

    在第一轮时,ii 左侧的靶位(靶位 i1i - 1)至多剩下一名 n+22nn + 2\sim 2n

    在第二轮时,ii 左侧的两个靶位总共至多剩下两名 n+22nn + 2\sim 2n

    特别地,在第 i1i - 1 轮时,靶位 1i11\sim i - 1 至多剩下 i1i - 1n+22nn + 2\sim 2n,且如果剩下 i1i - 1 名,则靶位 11 上至少有一名,此时我们再等一轮,第 ii 轮时,靶位 1i11\sim i - 1 至多剩下 i2i - 2n+22nn + 2\sim 2n

    以此类推,可知第 nn 轮时,ii 左侧的 n1n - 1 个靶位(也就是除了靶位 ii 本身的其它所有靶位)至多剩下 n2n - 2n+22nn + 2\sim 2n,但总共有 n1n - 1n+22nn + 2\sim 2n,这与靶位 ii 上没有 n+22nn + 2\sim 2n 矛盾了。\square

    这样,我们将 RR 替换为最小的 RnR'\geq n(或 2n2n)且 RR(modn)R\equiv R'\pmod n,答案不会改变。这给予我们 O(n3)\mathcal{O}(n ^ 3) 的模拟做法。

    考虑优化单次 O(n2)\mathcal{O}(n ^ 2) 的模拟。我们观察到,若主角的技术水平为 S1S_1,则在模拟的过程中,所有 >S1> S_1 的选手是等价的,称为 22 类选手;所有 <S1< S_1 的选手是等价的,称为 00 类选手。并且,根据移动规则,类似上述证明,若有 22 类选手进入靶位 2n2\sim n,则该靶位一直有 22 类选手;若有 11 类选手进入靶位 11,则该靶位一直有 11 类选手。

    因此,只要我们能够对于靶位 2n2\sim n 求出第一次有 22 类选手的时间 t2tnt_2\sim t_n,对于靶位 11 求出第一次有 11 类选手的时间 t1t_1,即可在任意时刻确定每个靶位上选手的类型,从而只维护主角的移动而不需要维护其它选手的移动。

    t1t_1 显然容易,考虑 t2tnt_2\sim t_n。设 sis_i 表示 22 类选手数量关于靶位的前缀和。我们认为,如果两个 22 类选手相遇,那么 初始位置靠后的选手获胜。这样,考虑最终移动到靶位 ii 上的选手初始靶位编号 jj

    • 如果 jjii 不经过靶位 11,则 jj 移动到 ii 的充要条件是 iji\sim j 至少有 ji+1j - i + 122 类选手,即 sjsi1j(i1)s_j - s_{i - 1} \geq j - (i - 1),且 恰好在第 ji+1j - i + 1 轮到达靶位 ii,每一步都向前移动 11。又因为每个靶位上至多有两个 22 类选手,所以符合条件的最小的 jj 一定满足 sjsi1=j(i1)s_j - s_{i - 1} = j - (i - 1)j(i1)+1j - (i - 1) + 1。实际上,特判掉靶位 ii 本来就有 22 类选手的情况之后,sjsi1s_j - s_{i - 1} 只能等于 j(i1)j - (i - 1)
    • 如果 jjii 经过靶位 11,那么稍微复杂一些。首先,jj 移动到 ii 的充要条件是 iji\sim j 至少有 jij - i22 类选手,即 sjsi1j(i1)1s_j - s_{i - 1} \geq j - (i - 1) - 1。类似地,除了靶位 nn 没有 22 类选手,靶位 11 有两个 22 类选手的情况,sjsi1s_j - s_{i - 1} 只能等于 j(i1)1j - (i - 1) - 1。但是,jj 并不一定恰好在第 ji+1j - i + 1 轮到达靶位 ii:如果 jj 在到达靶位 11 时,靶位 11 有两个选手,那么 jj 会在这里停顿一下,使得 jj 在第 ji+2j - i + 2 轮到达靶位 ii。易知其充要条件为靶位 1j1\sim j 初始全部有两个 22 类选手。

    求出 tt 之后,单次模拟的时间复杂度就可以做到 O(n)\mathcal{O}(n)

    因为超过一个 22 类选手只能打败他,所以在靶位 2n2\sim n 上,相较于靠前的起始位置,靠后的起始位置总要打败更多的 22 类选手。而在靶位 11 上,两个不同的起始位置可能等到相同的轮数才回到 nn,但靠后的起始位置一定不会在靠前起始位置之前(更少的轮数)回到 nn。因此,如果我们在线性的尺度下观察整个过程,设 fif_i 表示初始位置 ii 在线性尺度下的最终位置,即 ii 减去主角从 ii 出发经过 RR 轮的移动次数,那么对于 i<ji < jfifjf_i\leq f_j

    因为 R=O(n)R = \mathcal{O}(n),所以 fnf1=O(n)f_n - f_1 = \mathcal{O}(n)。我们枚举 [f1,fn)[f_1, f_n) 的所有 nn 的倍数 knkn,总共 O(1)\mathcal{O}(1) 个,二分求出第一个 ii 使得 fi>knf_i > kn,则初始位置 ii 以及对应的最终位置才有可能更新答案。时间复杂度 O(nlogn)\mathcal{O}(n\log n)

    #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
    上传者