E. Lost Array
题意
给你
n
n
n 个数,每次可以询问
k
(
1
≤
k
≤
n
)
k(1 \le k \le n)
k(1≤k≤n) 个数的异或和,要求用最少的询问得到所有
n
n
n 个数的异或和。如果没有可能的方案,则输出 -1
。
题解
- 异或奇数次等价异或一次,异或偶数次等价于没有异或;
- 这个问题等价于一个经典的问题,有 n n n 枚正面朝上的硬币,每次可以选择 k k k 个硬币翻转,怎么用最少的操作次数让所有硬币反面朝上;
- 枚举翻转中正面朝上的个数即可。
代码
#include <bits/stdc++.h>
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 505;
const int inf = 0x3f3f3f3f;
int n, k, dis[maxn], pre[maxn];
int flag[maxn];
struct node{
int cnt, step;
};
void bfs() {
memset(dis, inf, sizeof(dis));
memset(pre, -1, sizeof(pre));
queue<node> q;
dis[0] = 0; // i面朝上需要的最小步数
q.push({0, 0});
while (q.size()) {
node now = q.front();
q.pop();
if (dis[now.cnt] < now.step) continue;
rep(i, 0, k) {
if (i > now.cnt || k - i > n - now.cnt) continue;
int ncnt = now.cnt - i + k - i;
if (dis[ncnt] > now.step + 1) {
dis[ncnt] = now.step + 1;
q.push({ncnt, now.step + 1});
pre[ncnt] = now.cnt;
}
}
}
}
int query(int now, int nex) {
vector<int> q, vis(n + 5);
rep(i, 1, n) {
if (q.size() == k) break;
if (now < nex && flag[i] == 0 || now > nex && flag[i] == 1) {
flag[i] ^= 1;
now += (flag[i] ? 1 : -1);
q.push_back(i);
vis[i] = 1;
}
}
int c0 = (k - q.size()) / 2, c1 = c0;
rep(i, 1, n) {
if (vis[i]) continue;
if (c0 && flag[i] == 0) {
flag[i] ^= 1;
q.push_back(i);
c0--;
} else if (c1 && flag[i] == 1) {
flag[i] ^= 1;
q.push_back(i);
c1--;
}
}
cout << "?";
for (auto it : q) cout << " " << it;
cout << endl;
int ans; cin >> ans; return ans;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> k;
bfs();
if (dis[n] == inf) cout << "-1" << endl;
else {
vector<int> cnt;
for (int i = n; i != -1; i = pre[i])
cnt.push_back(i);
reverse(cnt.begin(), cnt.end());
int ans = 0;
rep(i, 1, cnt.size() - 1) {
ans ^= query(cnt[i - 1], cnt[i]);
}
cout << "! " << ans << endl;
}
return 0;
}