题目描述

$n = 3^m$ 个人在玩石头剪刀布,一共有 $t$ 游戏,每轮有 $m$ 石头剪刀布。

在同一轮的 $m$ 次游戏中,每个人的决策必须是提前确定的,也就是说不能采用随机策略,也不能根据前若干局的结果决定下一局的决策;这样,显然一共有 $n = 3^m$ 种决策。

这 $n = 3^m$ 个人会采取两两不同的决策。为了方便表达,对于第 $x$ ($0 \leq x < n$) 个人,将 $x$ 转换成三进制得到一个 $m$ 位的数。其中 $0$ 表示剪刀,$1$ 表示石头,$2$ 表示布。就得到了第 $x$ 个人采取的策略。

由于编号是固定的,因此每个人在不同轮的 $m$ 次游戏中都会采取同一套决策。

第 $x$ 个人在最初时有一个分数 $f_{0, x}$。

在第 $i$ 轮中,他将和另一个人 $y$ 进行 $m$ 次的石头剪刀布的比赛。

我们用 $W \left( x, y \right)$ 表示 $x$ 在和 $y$ 的 $m$ 次比赛中赢的次数;用 $L \left( x, y \right)$ 表示 $x$ 在和 $y$ 的 $m$ 次比赛中输的次数。

在第 $i$ 轮结束之后,第 $x$ 个人分数是:

$$ f_{i, x} = \sum_{0 \leq y < n} f_{i - 1, y} \cdot b_{u, v} $$

其中 $u = W \left( x, y \right) = L \left( y, x \right), v = L \left( x, y \right) = W \left( y, x \right)$,平局不计入次数;$b_{u, v}$ 则是一个给定的评分数组。

注意即使 $y$ 和 $x$ 一样 (自己转移到自己) 也会乘上一个系数 $b_{0, 0}$ (即自己跟自己打全为平局)。

显然随着轮数越来越多,分数也会越来越大,这个计分系统和我们平时用的计算机一样,也会溢出。当要储存的分数 $f$ 大于等于 $p$ 的时候,就会变成 $f \bmod p$。

B 君想知道 $t$ 轮之后所有人的分数,也就是 $f_{t, 0}, f_{t, 1}, \ldots, f_{t, n - 1}$。

G 君:「诶,我发现这个数 $p$ 有特殊的性质诶!不存在两个正整数,使得他们倒数的和等于 $\dfrac 3p$!」

B 君:「G 君好棒!不过这个题怎么做呢?」

输入格式

第一行包含三个非负整数 $m, t, p$ ($0 \leq m \leq 12; 0 \leq t \leq 10^9; 1 \leq p \leq 10^9$)。

第二行有 $n = 3^m$ 个整数,表示 $f_{0, 0}, f_{0, 1}, \cdots, f_{0, n - 1}$。保证 $0 \leq f_{0, i} < p$。

接下来的部分是一个数组 $b$,第 $1$ 行 $m + 1$ 个数,第 $2$ 行 $m$ 个数……第 $m + 1$ 行 $1$ 个数。

其中第 $i$ 行的第 $j$ 个数为 $b_{i - 1, j - 1}$($i, j \geq 1; i + j - 2 \leq m$),保证 $0 \leq b_{i, j} < p$。

其中 $p$ 满足,不存在正整数 $x, y > 0$,使得 $\dfrac 1x + \dfrac 1y = \dfrac 3p$

输出格式

输出 $n = 3^m$ 行,每行一个整数,表示每个人最终的得分。

其中第 $i$ 行表示第 $i$ 个人的得分 $f_{t, i}$。

题解

先来观察 $p$ 所满足的奇怪条件:不存在正整数 $x, y > 0$,使得 $\dfrac 1x + \dfrac 1y = \dfrac 3p$ (我很好奇 G 君是怎么一眼发现的),看看 $p$ 到底有什么神奇的性质。

若 $p = (3k - 1) r$,则取 $x = k r, y = k p$,则 $\dfrac 1x + \dfrac 1y = \dfrac 1 {k r} + \dfrac 1 {k p} = \dfrac {p + r} {k r p} = \dfrac 3p$,从而不满足条件。

若 $p = 3 r$,则取 $x = r + 1, y = r (r + 1)$,则 $\dfrac 1x + \dfrac 1y = \dfrac 1 {r + 1} + \dfrac 1 {r (r + 1)} = \dfrac 1r = \dfrac 3p$,也不满足条件。

这说明,$p$ 既不存在 $3k - 1$ 型的因子,也不存在 $3k$ 型的因子,从而 $p$ 的所有素因子均为 $6k + 1$ 型 (事实上,这个条件就是充要条件,充分性也可以证明,这里略去)。

但是这有什么用呢?我们等会儿再说,先去观察原题。


我们考虑一轮游戏,观察它对数组 $f_{i, x}$ 有什么影响。记 $A_x = f_{i-1, x}, C_x = f_{i, x}$。则转移即为

$$ \large C_x = \sum_{0 \leq y < n} A_y \cdot b_{W(x, y), W(y, x)} \tag 1 \label 1 $$

考虑如何处理 $b_{W(x, y), W(y, x)}$。令 $u = x \ominus_3 y$,其中 $\ominus_3$ 表示三进制不退位按位减法。可以发现,$x \ominus_3 y$ 的三进制表示中,$0, 1, 2$ 的个数分别表示 $x$ 与 $y$ 平、$x$ 赢 $y$、$x$ 输给 $y$ 的次数。

于是可以令 $\large B_{x \ominus_3 y} = b_{W(x, y), W(y, x)}$,则 $\eqref 1$ 式就变成

$$ \large C_k = \sum_{i \oplus_3 j = k} A_i B_j $$ 其中 $\oplus_3$ 表示三进制不进位加法。

看上去有没有很熟悉的感觉?对!如果中间的运算是 $\oplus$ (异或,二进制不进位加法) 而不是 $\oplus_3$ 的话,那就是一个集合对称差卷积!可以使用快速 Walsh 变换 (FWT) 解决。

那这回变成了三进制,该怎么办呢?

还是 FWT!做三进制的 FWT!

具体思路和二进制非常类似,这里就不再赘述。我们来考虑三进制 FWT 的基本变换——三个元素的变换。

设三个元素分别为 $a_0, a_1, a_2; b_0, b_1, b_2$,我们要得到 $c_0 = a_0 b_0 + a_1 b_2 + a_2 b_1; c_1 = a_0 b_1 + a_1 b_0 + a_2 b_2; c_2 = a_0 b_2 + a_1 b_1 + a_2 b_0$。

设变换 $T$ ($a \xrightarrow{T} A$) 使得 $A_i = a_0 + x a_1 + y a_2$,则 $B_i = b_0 + x b_1 + y b_2$,则 $C_i = A_i B_i = \left( a_0 + x a_1 + y a_2 \right) \left( b_0 + x b_1 + y b_2 \right) = c_0 + x c_1 + y c_2$。

于是有 $\begin{cases} x y = 1 \\ x^2 = y \\ y^2 = x \end{cases}$,即 $x^3 = 1$,故 $x = 1, \omega, \omega^2$ ($\omega = \dfrac {-1 + \sqrt 3 \mathrm i} 2$)。

我们取 $\begin{cases} A_0 = a_0 + a_1 + a_2 \\ A_1 = a_0 + \omega a_1 + \omega^2 a_2 \\ A_2 = a_0 + \omega^2 a_1 + \omega a_2 \end{cases}$,不难验证这个构造符合要求。

因此我们只需对一开始的 $f_0$ 做一遍快速 Walsh 变换,对 $B$ 数组也做一遍变换,接下来只需要使用基本的快速幂即可得到 $f_t$ 的变换,再逆变换回去即可。

在做逆变换的时候,最后需要乘 $n$ 的逆元。由于 $(3, p) = 1$,于是 $(n, p) = 1$,故 $n$ 存在逆元。


接下来考虑单位根如何处理的问题。如果暴力写个复数类也是可以的。不过这个模数的特殊性质,保证了在模意义下,$-3$ 一定是二次剩余。(那么接下来减一除以 $2$ 都可以做到,因为 $(2, p) = 1$)

由于 $n$ 的所有素因子都是 $6k + 1$ 型,先考虑 $6k + 1$ 型的素数 $p$。

$$ \left( \frac {-3} p \right) = \left( \frac {-1} p \right) \left( \frac 3p \right) = (-1)^{(p-1)/2} \cdot \left( \frac p3 \right) (-1)^{(3-1)/2 \cdot (p-1)/2} = (-1)^{p-1} \left( \frac p3 \right) = \left( \frac 13 \right) = 1 $$ (其中 $\left( \dfrac ap \right)$ 为 Legendre 符号)

再考虑这种素数的幂 $p^\alpha$,使用归纳法。对 $\alpha = 1$ 已经成立,现设 $-3$ 是 $p^\alpha$ 的二次剩余,证明 $-3$ 是 $p^{\alpha + 1}$ 的二次剩余:

由于 $x^2 \equiv -3 \pmod {p^\alpha}$,可设 $x^2 \equiv -3 + b p^\alpha \pmod {p^{\alpha + 1}}$。考虑方程 $\left( x + k p^\alpha \right)^2 \equiv -3 \pmod {p^{\alpha + 1}}$,可化简为 $-3 + b p^\alpha + 2 k x p^\alpha + k^2 p^{2 \alpha} \equiv -3 \pmod {p^{\alpha + 1}}$。

由于 $\alpha \geq 1$,所以 $2 \alpha \geq \alpha + 1$。故上述方程可以转化为 $2kx + b \equiv 0 \pmod p$,由于 $2x \not\equiv 0 \pmod p$,因此该方程对 $k$ 有唯一解,证毕。

接着考虑合数。由于每个满足题目条件的合数都能拆成上述素数或素数的幂的乘积,且它们两两互素。

由孙子定理 (中国剩余定理),可知 $-3$ 一定是该合数的二次剩余。

可以在 $O \left( \log n \right)$ 的时间内得到 $x^2 \equiv -3 \pmod n$ 的其中一个解,然后只需将 $2^{-1} (x - 1)$ 当作 $\omega$ 即可直接进行数值运算。

总时间复杂度 $O \left( \log p + 3^m \cdot m \log t \right)$。

代码

#include <bits/stdc++.h>
#define N 550000
#define M 15

typedef long long ll;

int mod;
int n = 1, m, T;
int f[N], g[N], d[M][M];
int pop1[N], pop2[N];
ll w, W;

ll PowerMod(ll a, int n, ll mod, ll c = 1) {for (; n; n >>= 1, a = a * a % mod) if (n & 1) c = c * a % mod; return c;}

namespace Quadratic {
	int p, s;

	inline void ensure(bool cond, const char *fmt, int para = 0) {if (!cond) printf(fmt, para), exit(0);}

	int exgcd(int a, int b, int &x, int &y) {
		if (b) {int d = exgcd(b, a % b, y, x); y -= a / b * x; return d;}
		else return x = 1, y = 0, a;
	}

	int GPowerMod(int a, int b, int n) {
		int ra = 1, rb = 0, t;
		for (; n; n >>= 1) {
			if (n & 1) {
				t = ((ll)a * rb + (ll)b * ra) % p;
				ra = ((ll)a * ra + (ll)b * rb % p * s) % p;
				rb = t;
			}
			t = 2ll * a * b % p;
			a = ((ll)a * a + (ll)b * b % p * s) % p;
			b = t;
		}
		return ra;
	}

	int get(int a, int p, int d) {
		int i, t, x, _ = a % p, fy = p, Fy = p * p;
		_ += _ >> 31 & mod; Quadratic::p = p;
		ensure(PowerMod(_, (p - 1) / 2, p) == 1, "FAIL get::Unknown Error (maybe there are factors without `6k+1`).");
		for (t = 0; ; ++t) {
			s = ((ll)t * t - _) % p; s += s >> 31 & mod;
			if (!s) break;
			if (PowerMod(s, (p - 1) / 2, p) == p - 1) break;
		}
		x = (s ? GPowerMod(t, 1, (p + 1) / 2) : t);
		for (i = 1; i < d; ++i, fy = Fy, Fy *= p)
			for (; ((ll)x * x - a) % Fy; x += fy);
		return x;
	}

	int combine(int a1, int m1, int a2, int m2) {
		int x, u1, u2;
		ensure(exgcd(m1, m2, u1, u2) == 1, "FAIL combine::Unknown Error.");
		x = ((ll)u2 % m1 * (a1 - a2) % m1) % m1;
		x = ((ll)x * m2 + a2) % ((ll)m1 * m2);
		return x + (x >> 31 & mod);
	}

	int main(int n) {
		int i, j, K, _n = n, u, cur = 0, mo = 1;
		ensure(n % 2, "FAIL n [equals to %d] is even.", n);
		for (i = 7; i * i <= n; i += 6)
			if (!(n % i)) {
				for (j = 0, K = 1; !(n % i); n /= i, ++j) K *= i;
				u = get(-3, i, j);
				cur = combine(cur, mo, u, K);
				mo *= K;
			}
		if (n > 1) {
			ensure(n % 6 == 1, "FAIL n [equals to %d] mod 6 is not equal to 1.", n);
			u = get(-3, n, 1);
			cur = combine(cur, mo, u, n);
		}
		ensure((cur * (ll)cur + 3) % _n == 0, "FAIL Unknown Error [result = %d].", cur);
		return (_n + 1ll) / 2 * (cur - 1) % mod;
	}
}

void FWT(int *f, bool rev = false) {
	int i, *j, *k, len = 1; ll u, v;
	for (i = 0; i < m; ++i) {
		for (j = f; j < f + n; j += len * 3)
			for (k = j; k < j + len; ++k) {
				u = (*k + k[len] * w + k[len * 2] * W) % mod;
				v = (*k + k[len] * W + k[len * 2] * w) % mod;
				*k = ((ll)*k + k[len] + k[len * 2]) % mod;
				rev ? (k[len] = v, k[len * 2] = u) : (k[len] = u, k[len * 2] = v);
			}
		len *= 3;
	}
}

int main() {
	int i, j; ll iv;
	scanf("%d%d%d", &m, &T, &mod); w = Quadratic::main(mod); W = w * w % mod;
	iv = PowerMod((2ll * mod + 1) / 3, m, mod);
	for (i = m; i; --i) n *= 3;
	for (i = 0; i < n; ++i) scanf("%d", f + i);
	for (i = 0; i <= m; ++i)
		for (j = 0; i + j <= m; ++j) scanf("%d", d[i] + j);
	for (i = 0; i < n; ++i) {
		pop1[i] = pop1[i / 3] + (i % 3 == 1);
		pop2[i] = pop2[i / 3] + (i % 3 == 2);
		g[i] = d[pop1[i]][pop2[i]];
	}
	FWT(f); FWT(g);
	for (i = 0; i < n; ++i) f[i] = PowerMod(g[i], T, mod, f[i]);
	FWT(f, true);
	for (i = 0; i < n; ++i) printf("%d\n", (int)(f[i] * iv % mod));
	return 0;
}

坑1:在 FWT 运算过程中有三个整数相加的情形,注意不要爆 int

坑2:FWT 的逆变换和二进制的有些许不同,要像 FFT 一样翻转 $A_1$ 和 $A_2$。