题意
有一个长度为(2 ^ n),下标依次为(0, 1, ldots, 2 ^ n - 1)的数组,你要和交互库进行若干轮操作。
每次操作开始时,数组里只有(a[x] = a[y] = frac{1}{sqrt 2} (x
eq y)),剩下的元素都是0。
你要和交互库进行多次交互,求出(x oplus y)的值(保证操作过程中(x oplus y)不变)。
你可以进行两种操作:
query
:
作一次询问,交互库会随机返回一个下标,返回(x)的概率是(frac{a[x] ^ 2}{sum_{i = 0} ^ {n - 1} a[i] ^ 2}),然后会开始新的一轮,交互库会重新ran一对(x, y),保证(x oplus y)不变的前提下,对数组进行同上的赋值。
manipulate(A, i)
:
给出一个(2 imes 2)的实数矩阵(A),交互库会把数组(a)更新,具体来说,即:
要求给出的矩阵是酉矩阵,即(A A ^T = I)。此时可以证明(sum_{i = 0} ^ {n - 1} a[i] ^ 2)不变。
(n leq 16)。
题解
首先单方面感谢yhx大佬,这份题解主要参考了他的博客。
这个manipulate
操作很像fwt_xor,而fwt_xor的矩阵是
这个矩阵不是酉矩阵。但是只要变换一下,乘个缩放因子即可
此时,设原序列为({a_i}),变换后序列为({a'_k}),根据结论,有
则根据已知,得
显然,({a'_k} ^ 2 = 0)或({a'_k} ^ 2 = frac{1}{2 ^ {n - 1}})。
注意到(sum {a'_k} ^ 2 = sum {a_k} ^ 2 = 1),因此有(2 ^ {n - 1})个(k)满足(a'_k = 0)和(2 ^ {n - 1})个(k)满足(a'_k
eq 0)。
此时,调用一次query
,会等概率返回(2 ^ {n - 1})个满足(a'_k
eq 0)的(k)中的一个。
又因为当(a'_k
eq 0)时,((-1) ^ {|x cap k|} = (-1) ^ {|y cap k|}),可以得到
注意到题目保证(x oplus y)始终不变,那么我们在得到一个(k)后,((x oplus y) cap k)的二进制中一定有偶数个1。
如果(k)是随机返回的,我们期望每次排除一半的位置。
期望(mathcal O(n))轮后找到答案,所以总操作次数期望(mathcal O(n ^ 2))。
期望时间复杂度为(mathcal O(n 2 ^ n))。
#include "quantumbreak.h"
#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 16;
const double s2 = 1 / sqrt(2.0);
double M[2][2] = {{s2, s2}, {s2, -s2}};
int query_xor(int n, int t) {
int S = (1 << n) - 1; bitset <N> vis;
for (int i = 1; i <= S; ++i) {
vis.set(i);
}
for ( ; vis.count() > 1; ) {
for (int i = 0; i < n; ++i) {
manipulate(M, i);
}
for (int r = query(), i = 1; i <= S; ++i) {
if (__builtin_parity(i & r)) {
vis.reset(i);
}
}
}
return vis._Find_first();
}