例如 $x = 2, y = 3$,那么 Alice 可以拿到 $x = 2, z = 3$ 而 Bob 拿到的是 $y = 3, z = 3$。 那么此时,Alice 可以知道,Bob 只有可能是 $3, 1$ 其中一个,故判断不出来,Alice 只能回复不确定 轮到 Bob 的时候,Bob 知道 Alice 可以是 $3, 2, 1, 0$ 中任意一个。但是 Alice 回复不知道,对于 Bob 而言,如果 Alice 是 $1, 0$ 的话,当她看到 $z = 3$,说明 Bob 至少为 $2$,故 Alice 应该可以确定,故可以排除掉这两个,所以 Alice 可以是 $3, 2$ 其中一个,但是还是不确定是否是相同还是 Alice 的更小,故也回复不知道 再轮到 Alice 了,如果 Bob 无法判断的话,那么 Bob 一定不是 $1$,理由同上了。故此时 Alice 才可以确定 Bob 一定是 $3$,那么就可以确定了 综上,需要 3 轮
这道题因为涉及到 OR 运算,所以很自然从二进制角度考虑问题,二进制的比较,无非就是从最高的 bit 位开始,谁先不是 1 了,谁就小了。
从 OR 运算考虑,若“我”拿到的那个值某一个 bit 位是 0,但是 OR 的结果是 1,那么显然,对方这一位是 1。同理,若 OR 的结果为 0,那么对方和我一样都是 0。问题就在“我”和 OR 的结果都是 1 的那些 bit 位。这个时候我并不确定对方是否是 1,这也是需要多轮博弈的地方。
如果这两个选择的值,在某个 bit 位之前都是相同的,且通过一段博弈之后相互确认相同了,但是在这个 bit 位下是是不同的,即一个为 0,一个是 1。那么对于那个为 0 的而言,必然可以立刻回答出答案,而那个为 1 的则仍然不确定,所以此时需要再加上 $1, 2$ 次判定,这取决于谁先回答。
而对于前面相同的部分,若为 0 的,不用猜疑也能知道对方的情况,故可以跳过,不需要猜,而为 1 的部分,则需要进行一次不确定的回答,才可以确认对方也是 1。注意,这里只需要一次回答,就可以让双方都知道对方那一位是 1 了。如果不确定,可以将上面的 case 反过来,让 Bob 先猜,则可以理解原因了。
voidsolve(){ int _; cin >> _; for (int ts = 0; ts < _; ++ts) { int n, mod = 998244353; cin >> n; vector<node> tree(n * 50); int mx = 0; auto newNode = [&]() { tree[mx].cnt = 0; tree[mx].zero = -1; tree[mx].one = -1; return mx++; };
int root = newNode(); auto add = [&](int x) { int cur = root; for (int i = 31; i >= 0; --i) { if (x & (1 << i)) cur = tree[cur].one == -1 ? tree[cur].one = newNode() : tree[cur].one; else cur = tree[cur].zero == -1 ? tree[cur].zero = newNode() : tree[cur].zero; tree[cur].cnt++; } };
for (int i = 0; i < n; ++i) { int tmp; cin >> tmp; add(tmp); }
int ans = 0, total = n * n; function<void(int, int)> dfs = [&](int cur, int deep) { if (tree[cur].zero == -1 && tree[cur].one == -1) { ans += deep * tree[cur].cnt * tree[cur].cnt; ans %= mod; return; } if (tree[cur].zero == -1) dfs(tree[cur].one, deep + 1); elseif (tree[cur].one == -1) dfs(tree[cur].zero, deep); else { ans += (2 * deep + 1) * tree[tree[cur].one].cnt * tree[tree[cur].zero].cnt; ans %= mod; dfs(tree[cur].one, deep + 1); dfs(tree[cur].zero, deep); } };
auto qp = [&](int a, int b) { if (b < 0) return0LL; int ret = 1; a %= mod; while (b) { if (b & 1) ret = (ret * a) % mod; b >>= 1; a = (a * a) % mod; } return ret; }; auto inv = [&](int a) { returnqp(a, mod - 2); };