CF2115D Gellyfish and Forget-Me-Not

题目链接

Part1:一些转化

为了使操作形式更加好看,我们在所有操作前,将答案异或上 \(\oplus_{i=1}^n b_i\)。然后令 \(c_i=a_i\oplus b_i\)。这样每次只需要决定一个 \(c_i\) 选不选就行了。

显然转化后与原问题等价。

  • 不选 \(c_i\),对应着选择 \(b_i\oplus 0=b_i\)
  • 选择 \(c_i\),对应着选择 \(b_i\oplus c_i=a_i\)

Part2:做法推导

运算与进制有关的,且答案求最值的,优先考虑从高位往低位贪心

先考虑 \(c_i\in [0,1]\) 怎么做。显然操作最后一个 \(c_i=1\) 的人能决定答案。带入到原问题中,二进制最高位是 \(0/1\)取决于最后一个最高位为 \(1\) 的数将要被谁操作

然而,前面那些最高位为 \(1\)\(c_i\),能带来的影响也并非没有变化。我们用 \(y\) 代表最后一个,\(x\) 代表前面的 \(c_i\)

为了方便考虑,假设前面的 \(x\) 唯一,且操作 \(y\) 的人希望最大化。注意到此时有 \(x\)\(y\) 不选,或 \(x\) 不选 \(y\) 选。两种情况相差一个 \(x\oplus y\) 的贡献。

换而言之,\(x\) 在此时的作用等价于一个 \(c_i=x\oplus y\) 的数。故而考虑将所有前面的 \(x\) 异或上 \(y\)

更低位可以效仿当前的做法。

最后将会存在若干能决定一个位是 \(0/1\) 的数,按决定的位从高往低依次模拟两个人怎么选就行了。

  • 注:这个过程其实很类似线性基

Part3:代码实现

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, LG = 60;
ll ans, a[N], b[N];
pair<ll, char> t[LG];
void Insert(ll x, char c) {
    FR(i, LG - 1, 0) {
        auto &[y, d] = t[i];
        if (x >> i & 1) {
            if (!y) {
                y = x, d = c;
                return;
            }
            x ^= y;
        }
    }
}
char c[N];
void Solve() {
    int n = ans = 0;
    scanf("%d", &n);
    FL(i, 1, n) scanf("%lld", &a[i]);
    FL(i, 1, n) scanf("%lld", &b[i]);
    scanf("%s", c + 1);
    FR(i, n, 1) {
        Insert(a[i] ^ b[i], c[i]);
        ans ^= b[i];
    }
    FR(i, LG - 1, 0) {
        auto &[y, d] = t[i];
        if (d == '0') {
            ans = min(ans, ans ^ y);
        } else {
            ans = max(ans, ans ^ y);
        }
        y = d = 0;
    }
    printf("%lld\n", ans);
}
int main() {
    int T;
	scanf("%d", &T);
	while (T--) {
		Solve();
	}
    return 0;
}
posted @ 2025-06-03 09:26  徐子洋  阅读(40)  评论(0)    收藏  举报