[HNOI2019]白兔之舞
題目傳送門
名字不錯 ^ o ^
分析
題意:較長,自己看!
聽說要單位根反演?不會.jpg。
還好有非單位根反演的做法:-)
首先樸素Dp的話大概就是
那麼大令人厭惡,給他生成函數掉!
設
式子化到這裏如果不是是多項式,我甚至想要矩乘!
那就試一試唄!
構造矩陣
可是多項式矩陣乘個鬼?
這個時候有一種神奇的操作,就是咱知道是次多項式,所以咱們可以把帶點值進去矩乘,然後再插回來。
帶一個點值的複雜度是的,帶點值的複雜度是,如果然後拉格朗日插值用優化的複雜度是,似乎蠻優秀的。。。。
在沒看數據範圍的情況下T-T
這個時候就得機智一點了,我們發現實際上我們僅僅需要知道意義下的多項式。
而且題目給了個條件
那不是明白着讓你帶原根進去嘛,假設原根爲,我們取,並帶入,這樣的話次冪在帶入的時候就自動循環了,就只需要帶個值,帶點值的部分變成了。
簡單地說,我們用帶入的方式對進行了一次
那麼怎麼求係數?根據的結論,只需要一次出來的點值就是係數
也就是已知和,求
然後關於卷積中項的處理,有一種操作是:
但是有點小問題,就是這道題如果有一個的話,可能找不到二次剩餘。
另一種巧妙的轉化方式是
這個轉化可以從組合意義的角度考慮。
這樣的話就有
根據套路設
把倍增反轉卷積即可。
事實上,上述描述的算法流程就是,一種用卷積實現任意長度和的方法。
代碼
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;
}