[HNOI2019]白兔之舞 Bluestein's Algorithm FFT 生成函數 矩陣乘法

[HNOI2019]白兔之舞

題目傳送門
名字不錯 ^ o ^

分析

題意:較長,自己看!
聽說要單位根反演?不會.jpg。
還好有非單位根反演的做法:-)
首先樸素Dp的話大概就是
F(i,j,l)=a=0i1b=1nF(a,b,l1)w[b][j]F(i,j,l)=\sum_{a=0}^{i-1}\sum_{b=1}^{n}F(a,b,l-1)w[b][j]
ll那麼大令人厭惡,給他生成函數掉!
F(i,j)=a=0i1b=1nxF(a,b)w[b][j]F(i,j)=\sum_{a=0}^{i-1}\sum_{b=1}^nxF(a,b)w[b][j]
=b=1n(a=0i1F(a,b))w[b][j]x=\sum_{b=1}^n(\sum_{a=0}^{i-1}F(a,b))w[b][j]x
S(i,b)=a=0iF(a,b)S(i,b)=\sum_{a=0}^{i}F(a,b)
F(i,j)=b=1nS(i1,b)w[b][j]xF(i,j)=\sum_{b=1}^nS(i-1,b)w[b][j]x
S(i,j)S(i1,j)=b=1nS(i1,b)w[b][j]xS(i,j)-S(i-1,j)=\sum_{b=1}^nS(i-1,b)w[b][j]x
式子化到這裏如果不是SS是多項式,我甚至想要矩乘!
那就試一試唄!
構造矩陣
[w[1][1]x+1w[1][2]xw[1][3]xw[2][1]xw[2][2]x+1w[2][3]xw[3][1]xw[3][2]xw[3][3]x+1] \left [ \begin{matrix} w[1][1]x+1 & w[1][2]x & w[1][3]x \\ w[2][1]x & w[2][2]x + 1 & w[2][3]x \\ w[3][1]x & w[3][2]x & w[3][3]x + 1 \end{matrix} \right ]
可是多項式矩陣乘個鬼?
這個時候有一種神奇的操作,就是咱知道F(L,i)F(L,i)LL次多項式,所以咱們可以把xx帶點值進去矩乘,然後再插回來。
帶一個點值的複雜度是O(n3logL)O(n^3logL)的,帶點值的複雜度是O(Ln3LogL)O(Ln^3LogL),如果然後拉格朗日插值用FFTFFT優化的複雜度是O(Llog2L)O(Llog^2L),似乎蠻優秀的。。。。
在沒看數據範圍的情況下T-T
這個時候就得機智一點了,我們發現實際上我們僅僅需要知道mod  xk\mod x^k意義下的多項式。
而且題目給了個條件kp1k|p-1
那不是明白着讓你帶原根進去嘛,假設原根爲gg,我們取w=gp1kw=g^{\frac{p-1}{k}},並帶入x=w0,w1wkx=w^0,w^1\cdots w^k,這樣的話次冪在帶入的時候就自動循環了,就只需要帶kk個值,帶點值的部分變成了kn3logLkn^3logL
簡單地說,我們用帶入的方式對F(L,i)F(L,i)進行了一次DFTDFT
那麼怎麼求係數?根據FFTFFT的結論,只需要IDFTIDFT一次出來的點值就是係數
也就是已知F(i)=i=0k1aixiF(i)=\sum_{i=0}^{k-1}a_ix^iww,求
G(i)=i=0k11kF(wi)xi=1ki=0k1j=0k1wijajG(i)=\sum_{i=0}^{k-1}\frac{1}{k}F(w^{-i})x^i=\frac{1}{k}\sum_{i=0}^{k-1}\sum_{j=0}^{k-1}w^{-ij}a_j
然後關於卷積中ijij項的處理,有一種操作是:
ij=(i+j)2i2j22ij=\frac{(i+j)^2-i^2-j^2}{2}
但是有點小問題,就是這道題如果有一個12\frac{1}{2}的話,可能找不到二次剩餘。
另一種巧妙的轉化方式是ij=Ci+j2Ci2Cj2ij=C_{i+j}^2-C_i^2-C_j^2
這個轉化可以從組合意義的角度考慮。
這樣的話就有
G(i)=1ki,jwCi2+Cj2Ci+j2aj=1kwCi2i,jwCi+j2wCj2ajG(i)=\frac{1}{k}\sum_{i,j}w^{C_i^2+C_j^2-C_{i+j}^2}a_j=\frac{1}{k}w^{C_i^2}\sum_{i,j}w^{-C_{i+j}^2}w^{C_j^2}a_j
根據套路設fi=wCi2ai,gi=wCi2f_i=w^{C_i^2}a_i,g_i=w^{-C_{i}^2}
gg倍增反轉卷積即可。
事實上,上述描述的算法流程就是BluesteinsAlgorithmBluestein's Algorithm,一種用卷積實現任意長度DFTDFTIDFTIDFT的方法。

代碼

MTT好煩啊

#include<bits/stdc++.h>
const int N = 262144;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int st[N], w[N], F[N], G[N], H[N], v[3][3], tp, P, g, n, wn, L, k, x, y;
struct Maxtir {
	int m[3][3];
	int *operator [] (int x) {return m[x];}
	Maxtir operator * (const Maxtir &B) {
		static Maxtir C;
		for(int i = 0;i < n; ++i)
			for(int j = 0;j < n; ++j) {
				long long res = 0;
				for(int k = 0;k < n; ++k)
					res += 1LL * m[i][k] * B.m[k][j];
				C[i][j] = res % P;
			}
		return C;
	}
}A, B;
void Pow(int k) {
	B = A; --k;
	for(;k; B = B * B, k >>= 1)
		if(k & 1)
			A = A * B;
}
int Pow(int x, int k) {
	int r = 1;
	for(;k; x = 1LL * x * x % P, k >>= 1)
		if(k & 1)
			r = 1LL * r * x % P;
	return r;
}
void FindG(int P) {
	int m = sqrt(P - 1);
	for(int i = 2;i <= m; ++i)
		if(!((P - 1) % i)) {
			st[++tp] = i;
			if(i * i != P - 1) 
				st[++tp] = (P - 1) / i;
		}
	for(g = 2;; ++g) {
		int j;
		for(j = 1;j <= tp; ++j)
			if(Pow(g, st[j]) == 1)
				break;
		if(j == tp + 1) return;
	}	
}
void Calc(int w) {
	for(int i = 0;i < n; ++i)
		for(int j = 0;j < n; ++j)
			A[i][j] = (1LL * w * v[i][j] + (i == j)) % P;
	Pow(L); 
}
int C2(int i) {return (1LL * i * (i - 1) >> 1) % k;}
namespace MTT {
	const double pi = acos(-1.0);
	int R[N];
	struct cp {
		double r, i;
		cp(double _r = 0, double _i = 0) : r(_r), i(_i) {}
		cp operator + (const cp &a) const {return cp(r + a.r, i + a.i);}
		cp operator - (const cp &a) const {return cp(r - a.r, i - a.i);}
		cp operator * (const cp &a) const {return cp(r * a.r - i * a.i, r * a.i + i * a.r);}
	}A[N], B[N], C[N], D[N], w[N];
	void Pre(int m) {
		int x = 0; L = 1;
		for(;(L <<= 1) < m;) ++x;
		for(int i = 1;i < L; ++i)
			R[i] = R[i >> 1] >> 1 | (i & 1) << x;
		for(int i = 0;i < L; ++i)
			w[i] = cp(cos(2 * pi * i / L), sin(2 * pi * i / L));
	}
	void DFT(cp *F) {
		for(int i = 0;i < L; ++i)
			if(i < R[i])
				std::swap(F[i], F[R[i]]);
		for(int i = 1, d = L >> 1; i < L; i <<= 1, d >>= 1)
			for(int j = 0;j < L; j += i << 1) {
				cp *l = F + j, *r = F + j + i, *p = w, tp;
				for(int k = i; k--; ++l, ++r, p += d)
					tp = *r * *p, *r = *l - tp, *l = *l + tp;
			}
	}
	void Mul(int *F, int n, int *G, int m, int *H) {
		Pre(n + m);
		for(int i = 0;i < n; ++i)
			A[i] = F[i] & 32767, B[i] = F[i] >> 15;
		for(int i = 0;i < m; ++i)
			C[i] = G[i] & 32767, D[i] = G[i] >> 15;
		DFT(A); DFT(B); DFT(C); DFT(D);
		for(int i = 0;i < L; ++i) {
			const cp a = A[i], b = B[i], c = C[i], d = D[i];
			A[i] = a * c; B[i] = a * d + b * c; C[i] = b * d;
		}
		DFT(A); DFT(B); DFT(C);
		for(int i = 0;i < n + m; ++i) {
			int j = L - i & L - 1;
			long long a = (long long)(A[j].r / L + 0.5) % P;
			long long b = (long long)(B[j].r / L + 0.5) % P;
			long long c = (long long)(C[j].r / L + 0.5) % P;
			H[i] = (a + (b << 15) % P + (c << 30) % P) % P;
		}
	}
}
int main() {
	n = ri(); k = ri(); L = ri(); x = ri() - 1; y = ri() - 1; P = ri();
	for(int i = 0;i < n; ++i)
		for(int j = 0;j < n; ++j)
			v[i][j] = ri();
	FindG(P); wn = Pow(g, (P - 1) / k);
	w[0] = 1; 
	for(int i = 1;i < k; ++i)
		w[i] = 1LL * w[i - 1] * wn % P;
	for(int i = 0;i < k; ++i) {
		Calc(w[i]);
		G[k - i - 1] = 1LL * A[x][y] * w[C2(i)] % P;
	}
	for(int i = 0;i < (k << 1 | 1); ++i)
		F[i] = w[(k - C2(i)) % k];
	MTT::Mul(F, k << 1 | 1, G, k, H);
	int invk = Pow(k, P - 2);
	for(int i = 0;i < k; ++i)
		printf("%d\n", 1LL * H[i + k - 1] * invk % P * w[C2(i)] % P); 
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章