[HNOI2019]白兔之舞

题解

我们枚举 y y y 坐标序列,并使用组合数计算 x x x 坐标序列的个数,所以我们要对 ∀ 0 ≤ t < k \forall 0\le t<k 0t<k

∑ m   m o d   k = t ∑ a 0 = x , a 1 , a 2 , ⋯   , a m = y ( L m ) ∏ i = 1 m − 1 w ( a i , a i + 1 ) \sum_{m\bmod k=t} \sum_{a_0=x,a_1,a_2,\cdots,a_m=y} \binom{L}{m} \prod_{i=1}^{m-1} w(a_i,a_{i+1}) mmodk=ta0=x,a1,a2,,am=y(mL)i=1m1w(ai,ai+1)

求出,由于 n n n 很小容易联想到矩阵乘法,令 G G G 为给定矩阵,则将上式改写为

[ G x , y ] ∑ m   m o d   k = t ( L m ) G m [G_{x,y}] \sum_{m\bmod k=t} \binom{L}{m} G^m [Gx,y]mmodk=t(mL)Gm

(dbq, [ G x , y ] [G_{x,y}] [Gx,y] 这个记号是我瞎掰的)

ω \omega ω k k k 次单位根,作用单位根反演:

[ G x , y ] ∑ m ≥ 0 ( L m ) G m 1 k ∑ j = 0 k − 1 ω ( m − t ) j = [ G x , y ] 1 k ∑ j = 0 k − 1 ω − t j ( G ω j + I ) L \begin{aligned} & [G_{x,y}] \sum_{m\ge 0} \binom{L}{m} G^m \frac{1}{k} \sum_{j=0}^{k-1} \omega^{(m-t)j} \\ &= [G_{x,y}] \frac{1}{k} \sum_{j=0}^{k-1} \omega^{-tj} (G\omega^{j}+I)^L \end{aligned} [Gx,y]m0(mL)Gmk1j=0k1ω(mt)j=[Gx,y]k1j=0k1ωtj(Gωj+I)L

t j = [ G x , y ] ( G ω j + 1 ) L t_j=[G_{x,y}](G\omega^j+1)^L tj=[Gx,y](Gωj+1)L,使用技巧 i j = ( i + j 2 ) − ( i 2 ) − ( j 2 ) ij=\binom{i+j}{2}-\binom{i}{2}-\binom{j}{2} ij=(2i+j)(2i)(2j),于是上式化为

1 k ∑ j = 0 k − 1 ω − ( t + j 2 ) + ( t 2 ) + ( j 2 ) t j = 1 k ω ( t 2 ) ∑ j = 0 k − 1 ( t j ω ( j 2 ) ) ω − ( t + j 2 ) \begin{aligned} & \frac{1}{k} \sum_{j=0}^{k-1} \omega^{-\binom{t+j}{2}+\binom{t}{2}+\binom{j}{2}}t_j \\ &= \frac{1}{k} \omega^{\binom{t}{2}} \sum_{j=0}^{k-1} (t_j\omega^{\binom{j}{2}})\omega^{-\binom{t+j}{2}} \end{aligned} k1j=0k1ω(2t+j)+(2t)+(2j)tj=k1ω(2t)j=0k1(tjω(2j))ω(2t+j)

将其中一个序列反转,比如令 f j = t j ω ( j 2 ) = [ G x , y ] ( G ω j + I ) L ω ( j 2 ) ( 0 ≤ j < k ) , g 2 k − 2 − j = ω − ( j 2 ) f_j=t_j\omega^{\binom{j}{2}}=[G_{x,y}](G\omega^{j}+I)^L\omega^{\binom{j}{2}} (0\le j<k), g_{2k-2-j}=\omega^{-\binom{j}{2}} fj=tjω(2j)=[Gx,y](Gωj+I)Lω(2j)(0j<k),g2k2j=ω(2j),则上式将化为卷积:

1 k ω ( t 2 ) ∑ j = 0 k − 1 f j g 2 k − 2 − t − j \frac{1}{k} \omega^{\binom{t}{2}} \sum_{j=0}^{k-1} f_jg_{2k-2-t-j} k1ω(2t)j=0k1fjg2k2tj

由于我们只用到了单位根反演中的性质,且 k ∣ ( p − 1 ) k|(p-1) k(p1),所以我们可以使用模 p p p 意义下的原根 g g g 代替单位根。

接下来我们需要进行模 p p p 意义下的卷积,使用 MTT 或者三模 NTT 都可以解决。 O ( k log ⁡ k ) O(k\log k) O(klogk)

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef long double lb;
const int MAXN = 6e5 + 5;
const int MOD = 998244353;
const lb pi = acos(-1);

ll q_pow(ll a, ll b, ll p) {
	ll ret = 1;
	for (; b; a = a * a % p, b >>= 1) if (b & 1) ret = ret * a % p;
	return ret;
}
ll inv(ll x, ll p) { return q_pow(x, p - 2, p); }
inline void ADD(int& x, int y, int p) { x += y; if (x >= p) x -= p; }

int getGen(int p) {
	int g;
	for (g = 2; g <= p; ++g) {
		int tmp = p - 1; bool flag = 1;
		for (int i = 2; i * i <= tmp; ++i) if (tmp % i == 0) {
			while (tmp % i == 0) tmp /= i;
			if (q_pow(g, (p - 1) / i, p) == 1) { flag = 0; break; }
		}
		if (flag) break;
	}
	return g;
}

int N, K, L, X, Y, P, Omega;
struct matrix {
	int a[3][3];
	matrix () { for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) a[i][j] = 0; }
	matrix operator * (int k) {
		matrix C;
		for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) C.a[i][j] = 1ll * a[i][j] * k % P;
		return C;
	}
	friend matrix operator* (const matrix &A, const matrix& B) {
		matrix C;
		for (int i = 0; i < N; ++i) for (int k = 0; k < N; ++k) for (int j = 0; j < N; ++j)
			ADD(C.a[i][j], 1ll * A.a[i][k] * B.a[k][j] % P, P);
		return C;
	}
	matrix operator ^ (int k) {
		matrix RET, A = *this;
		for (int i = 0; i < N; ++i) RET.a[i][i] = 1;
		for (; k; A = A * A, k >>= 1) if (k & 1) RET = RET * A;
		return RET;
	}
} mat;

struct comp {
	lb x, y;
	comp (lb x = 0, lb y = 0) : x(x), y(y) {}
	friend comp operator+ (const comp& l, const comp& r) { return comp(l.x + r.x, l.y + r.y); }
	friend comp operator- (const comp& l, const comp& r) { return comp(l.x - r.x, l.y - r.y); }
	friend comp operator* (const comp& l, const comp& r) { return comp(l.x * r.x - l.y * r.y, l.x * r.y + l.y * r.x); }
};

int rev[MAXN];
void FFT(comp* A, int LIM, int op) {
	for (int i = 0; i < LIM; ++i) if (rev[i] < i) swap(A[rev[i]], A[i]);
	for (int l = 2; l <= LIM; l <<= 1) {
		comp g(cos(2.0 * pi / l), op * sin(2.0 * pi / l));
		for (int i = 0; i < LIM; i += l) {
			comp wn(1, 0);
			for (int j = i; j < i + (l >> 1); ++j, wn = wn * g) {
				comp x = A[j], y = wn * A[j + (l >> 1)];
				A[j] = x + y, A[j + (l >> 1)] = x - y;
			}
		}
	}
	if (op == -1) {
		for (int i = 0; i < LIM; ++i) A[i].x /= LIM, A[i].y /= LIM;
	}
}
comp A0[MAXN], A1[MAXN], B0[MAXN], B1[MAXN];
void MTT(int* A, int* B, int DEG) {
	int LIM = 1, L = 0;
	while (LIM <= DEG * 2) LIM <<= 1, ++L;
	for (int i = 0; i < LIM; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1));
	for (int i = 0; i < LIM; ++i) {
		A0[i] = A[i] >> 15, A1[i] = A[i] & ((1 << 15) - 1);
		B0[i] = B[i] >> 15, B1[i] = B[i] & ((1 << 15) - 1);
	}
	FFT(A0, LIM, 1), FFT(A1, LIM, 1), FFT(B0, LIM, 1), FFT(B1, LIM, 1);
	for (int i = 0; i < LIM; ++i) {
		comp a0 = A0[i], a1 = A1[i], b0 = B0[i], b1 = B1[i];
		A0[i] = a0 * b0, A1[i] = a0 * b1, B0[i] = a1 * b0, B1[i] = a1 * b1;
	}
	FFT(A0, LIM, -1), FFT(A1, LIM, -1), FFT(B0, LIM, -1), FFT(B1, LIM, -1);
	for (int i = 0; i < LIM; ++i) {
		ll t1 = (ll)(A0[i].x + 0.5) % P, t2 = (ll)(A1[i].x + 0.5) % P;
		ll t3 = (ll)(B0[i].x + 0.5) % P, t4 = (ll)(B1[i].x + 0.5) % P;
		A[i] = (((t1 << 30) + ((t2 + t3) << 15) + t4) % P + P) % P;
	}
}

int F[MAXN], G[MAXN];
int main() {
	scanf("%d%d%d%d%d%d", &N, &K, &L, &X, &Y, &P), --X, --Y;
	int Gp = getGen(P); Omega = q_pow(Gp, (P - 1) / K, P);
	for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) scanf("%d", &mat.a[i][j]);
	for (int i = 0; i < K; ++i) {
		matrix tmp = mat; int pw = q_pow(Omega, i, P);
		for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) tmp.a[i][j] = 1ll * tmp.a[i][j] * pw % P;
		for (int i = 0; i < N; ++i) ADD(tmp.a[i][i], 1, P);
		tmp = tmp ^ L;
		F[i] = 1ll * tmp.a[X][Y] * q_pow(Omega, 1ll * i * (i - 1) / 2, P) % P;
	}
	for (int i = 0; i <= 2 * K - 2; ++i) G[2 * K - 2 - i] = q_pow(inv(Omega, P), 1ll * i * (i - 1) / 2, P);
	MTT(F, G, 2 * K);
	int invK = inv(K, P);
	for (int i = 0; i < K; ++i) {
		printf("%d\n", 1ll * invK * q_pow(Omega, 1ll * i * (i - 1) / 2, P) % P * F[2 * K - 2 - i] % P);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值