【集训队作业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】量子破碎 提到的矩阵就可以了:

\[A = \left[ \begin{matrix} \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} & -\frac{1}{\sqrt{2}} \end{matrix} \right] \]

虽然有 \(\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 做了两步:

  1. 对于所有第 \(i\) 位为 \(1\)\(j\),都乘上 \(\omega_{2^{i+1}}^{j} = \omega_{2^{i+1}}^{j \pmod {2^{i+1}}}\)
  2. 对于所有第 \(i\) 位为 \(1\)\(j\),用CR调用FWT矩阵,也就是 \(l = x + y, r = x - y\)

第二步显然在这一层过后调用一次 CR(i, i, FWT) 即可。

第一步的构造方法和我们构造点值序列类似,也是按位乘上去。只要对于每一个 \(j < i\),取矩阵

\[A = \left[ \begin{matrix} 1 & 0\\ 0 & \omega_{2^{i+1}}^{2^j} \end{matrix} \right] \]

显然是个酉矩阵。

然后需要第 \(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));
}
posted @ 2019-10-08 22:37  daklqw  阅读(328)  评论(0编辑  收藏  举报