【集训队作业2018】不可名状
现在有用的只有 CU, CR。QR只能用一次,说明我们要用确定性的做法,而不能依赖于期望。
记答案为 \(c=\omega_n^b\)。和答案有关的操作只有 CU,QR返回值只和下标有关,说明需要让只有第 \(b\) 位有值。但是如何把值转换为下标?
根据DFT的经验,或许我们可以构造出 \(f\left(x\right) = x^b\) 的点值序列,然后做一遍IDFT,这样就可以把值序列转换为单个下标了。
这也是看到这道题容易想到的一个做法。
现在的问题就是构造出点值序列,然后还有IDFT。因为有 ACR 操作,直接套IDFT定义式,那么只要构造出点值序列就能很方便地骗到 \(62\) 分了。
将函数代入DFT矩阵,容易发现DFT后的序列为 \(a_i = \omega_n^{ib} = \left(\omega_n^a\right)^i = c^i\),也就是我们需要让 \(a_i = c^i\)。
这个很好办,就是先用CR使每一位都为 \(1\),然后调用 CU(i, 1 << i)
。
如何用 CR 使每一位都为 \(1\) ?套用我们现有的 FWT 矩阵,对于集合幂级数 \(f(x) = x^\emptyset\) FWT的结果就是 \(\widehat{f}(x) = \sum_{S \in U} x^S\)。所以用 【UTR #3】量子破碎 提到的矩阵就可以了:
虽然有 \(\left(\frac{1}{\sqrt{2}}\right)^n\) 的常数差异,但对答案没影响。
现在需要考虑 IDFT。考虑直接FFT。那么首先要 bitreverse。这很简单,我们把上面的 CU(i, 1 << i)
改为 CU(i, 1 << 15 - i)
,那么每一位都是 bitreverse 后的结果了。
现在考虑 FFT 做 IDFT 的代码(代码随便打的)
for (int mid = 1; mid != lim; mid <<= 1) {
const double deg = PI / mid;
const complex wn(cos(deg), -sin(deg));
for (int k = 0; k != lim; k += mid << 1) {
complex w(1, 0);
for (int l = 0; l != mid; ++l, w = w * wn) {
complex x = A[l + k], y = A[l + k + mid] * w;
A[l + k] = x + y;
A[l + k + mid] = x - y;
}
}
}
容易发现,实际上这个 FFT 做了两步:
- 对于所有第 \(i\) 位为 \(1\) 的 \(j\),都乘上 \(\omega_{2^{i+1}}^{j} = \omega_{2^{i+1}}^{j \pmod {2^{i+1}}}\)。
- 对于所有第 \(i\) 位为 \(1\) 的 \(j\),用CR调用FWT矩阵,也就是 \(l = x + y, r = x - y\)。
第二步显然在这一层过后调用一次 CR(i, i, FWT)
即可。
第一步的构造方法和我们构造点值序列类似,也是按位乘上去。只要对于每一个 \(j < i\),取矩阵
显然是个酉矩阵。
然后需要第 \(j\) 位也有才能乘上,所以需要调用 CR(j, i, IDFT)
然后,QR一次然后直接返回即可。就这样做完了这道题。
询问复杂度 \(O(n ^ 2)\),可以通过此题。
这道题咕了好久天,我连FFT都不会了/px,调了好久/px
还是冷静思考这个方法有用
#include <bits/stdc++.h>
#include "unnamable.h"
typedef std::complex<double> C;
const C is2(1 / sqrtl(2), 0);
C fwt[2][2] = {
{is2, is2},
{is2, -is2}
} ;
C fft[2][2];
const double PI = acosl(-1);
const int MN = 16;
C SOL(int typ) {
INI(MN);
for (int i = 0; i != MN; ++i)
CR(i, i, fwt);
for (int i = 0; i != MN; ++i)
CU(i, 1 << MN - 1 - i);
fft[0][0] = C(1, 0);
const int U = 1 << MN;
for (int i = 0; i != MN; ++i) {
const int mid = 1 << i;
const double dwn = PI / mid;
for (int j = 0; j < i; ++j) {
double deg = dwn * (1 << j);
fft[1][1] = C(cosl(deg), -sinl(deg));
CR(j, i, fft);
}
CR(i, i, fwt);
}
int ans = QR();
double deg = PI * 2 / (1 << MN) * ans;
return C(cosl(deg), sinl(deg));
}