P8523 [IOI 2021] 位移寄存器 题解

Description

\(9\) 种位运算操作,利用这 9 种操作实现数组取最小值和数组排序。

Solution

首先考虑对于两个数怎么求较小值。由于这些操作中没有比较操作,很难办。

不妨设第一个为 \(x\),第二个为 \(y\)

注意到有加法操作,这启发我们用加法判断大小,即如果 \(x+(2^k-1-y)\) 有进位,则 \(x>y\),否则 \(x\leq y\),可以用一次与操作取出进位的值,设 \(s=[x\leq y]\),则 \(x\leftarrow x\oplus(s\&(x\oplus y))\),也可以直接做。

如果有很多数就考虑倍增,先把 \(n\) 变为 \(2\) 的次幂,然后每次对相邻的两个数一起做上面的操作即可。

对于排序,可以奇数轮冒泡 \([1,n]\) 的相邻位置,偶数轮冒泡 \([2,n-1]\) 的相邻位置,容易发现只需要做至多 \(n\) 轮,显然能过。

Code

#include "registers.h"
#include <bits/stdc++.h>

#ifdef ORZXKR
#include "grader.cpp"
#endif

void solve1(int s, int n, int k, int q) {
  if (n <= 1) return;
  std::vector<bool> v(2000, 0);
  {
    int len = 1;
    for (; len < n; len <<= 1) {}
    for (int i = 0; i < 2000; ++i) v[i] = 0;
    for (int i = n; i < len; ++i)
      for (int j = 0; j < k; ++j)
        v[i * k + j] = 1;
    append_store(1, v);
    append_or(0, 0, 1);
    n = len;
  }
  for (int b = 1; b < n; b <<= 1) {
    append_right(10, 0, b * k);
    append_not(1, 10);
    for (int i = 0; i < 2000; ++i) v[i] = 0;
    for (int i = 0; i < n; i += 2 * b)
      for (int j = 0; j < k; ++j)
        v[i * k + j] = 1;
    append_store(2, v);
    append_and(3, 0, 2);
    append_and(4, 1, 2);
    append_add(5, 3, 4);
    for (int i = 0; i < 2000; ++i) v[i] = 0;
    for (int i = 1; i < n; i += 2 * b) v[i * k] = 1;
    append_store(6, v);
    append_and(7, 5, 6);
    append_right(8, 7, k);
    {
      int len = 1;
      for (; len < k;) {
        int x = std::min(k - len, len);
        append_left(9, 8, x);
        append_or(8, 8, 9);
        len += x;
      }
    }
    append_xor(10, 0, 10);
    append_and(11, 8, 10);
    append_xor(0, 0, 11);
  }
}

void solve2(int s, int n, int k, int q) {
  if (n <= 1) return;
  std::vector<bool> v(2000, 0);
  {
    int len = 1;
    for (; len < n; len <<= 1) {}
    for (int i = 0; i < 2000; ++i) v[i] = 0;
    for (int i = n; i < len; ++i)
      for (int j = 0; j < k; ++j)
        v[i * k + j] = 1;
    append_store(1, v);
    append_or(0, 0, 1);
    n = len;
  }
  for (int c = 0; c < n; ++c) {
    if (~c & 1) {
      append_right(10, 0, k);
      append_left(12, 0, k);
      append_xor(12, 0, 12);
      append_not(1, 10);
      for (int i = 0; i < 2000; ++i) v[i] = 0;
      for (int i = 0; i < n; i += 2)
        for (int j = 0; j < k; ++j)
          v[i * k + j] = 1;
      append_store(2, v);
      append_and(3, 0, 2);
      append_and(4, 1, 2);
      append_add(5, 3, 4);
      for (int i = 0; i < 2000; ++i) v[i] = 0;
      for (int i = 1; i < n; i += 2) v[i * k] = 1;
      append_store(6, v);
      append_and(7, 5, 6);
      for (int i = 0; i < 2000; ++i) v[i] = 0;
      for (int i = 1; i < n; i += 2)
        for (int j = 0; j < k; ++j)
          v[i * k + j] = 1;
      append_store(6, v);
      append_and(12, 6, 12);
      append_right(8, 7, k);
      {
        int len = 1;
        for (; len < k;) {
          int x = std::min(k - len, len);
          append_left(9, 8, x);
          append_or(8, 8, 9);
          len += x;
        }
      }
      append_xor(10, 0, 10);
      append_and(11, 8, 10);
      append_xor(0, 0, 11);
      append_and(0, 0, 2);
      append_left(13, 0, k);
      append_xor(14, 12, 13);
      append_or(0, 0, 14);
    } else {
      if (n <= 2) continue;
      append_move(15, 0);
      append_right(10, 15, k);
      append_left(12, 15, k);
      append_xor(12, 15, 12);
      append_not(1, 10);
      for (int i = 0; i < 2000; ++i) v[i] = 0;
      for (int i = 1; i < n - 1; i += 2)
        for (int j = 0; j < k; ++j)
          v[i * k + j] = 1;
      append_store(2, v);
      append_and(3, 15, 2);
      append_and(4, 1, 2);
      append_add(5, 3, 4);
      for (int i = 0; i < 2000; ++i) v[i] = 0;
      for (int i = 2; i < n - 1; i += 2) v[i * k] = 1;
      append_store(6, v);
      append_and(7, 5, 6);
      for (int i = 0; i < 2000; ++i) v[i] = 0;
      for (int i = 2; i < n - 1; i += 2)
        for (int j = 0; j < k; ++j)
          v[i * k + j] = 1;
      append_store(6, v);
      append_and(12, 6, 12);
      append_right(8, 7, k);
      {
        int len = 1;
        for (; len < k;) {
          int x = std::min(k - len, len);
          append_left(9, 8, x);
          append_or(8, 8, 9);
          len += x;
        }
      }
      append_xor(10, 15, 10);
      append_and(11, 8, 10);
      append_xor(15, 15, 11);
      append_and(15, 15, 2);
      append_left(13, 15, k);
      append_xor(14, 12, 13);
      append_or(15, 15, 14);
      for (int i = 0; i < 2000; ++i) v[i] = 1;
      for (int i = 1; i < n - 1; ++i)
        for (int j = 0; j < k; ++j)
          v[i * k + j] = 0;
      append_store(16, v);
      append_and(0, 0, 16);
      append_not(16, 16);
      append_and(15, 15, 16);
      append_xor(0, 0, 15);
    }
  }
}

void construct_instructions(int s, int n, int k, int q) {
  if (s == 0) solve1(s, n, k, q);
  else solve2(s, n, k, q);
}
posted @ 2025-08-04 20:46  下蛋爷  阅读(15)  评论(0)    收藏  举报