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);
}

浙公网安备 33010602011771号