题目传送门
名字不错 ^ o ^
题意:较长,自己看!
听说要单位根反演?不会.jpg。
还好有非单位根反演的做法:-)
首先朴素Dp的话大概就是
F ( i , j , l ) = ∑ a = 0 i − 1 ∑ b = 1 n F ( a , b , l − 1 ) w [ b ] [ j ] F(i,j,l)=\sum_{a=0}^{i-1}\sum_{b=1}^{n}F(a,b,l-1)w[b][j] F(i,j,l)=∑a=0i−1∑b=1nF(a,b,l−1)w[b][j]
l l l那么大令人厌恶,给他生成函数掉!
F ( i , j ) = ∑ a = 0 i − 1 ∑ b = 1 n x F ( a , b ) w [ b ] [ j ] F(i,j)=\sum_{a=0}^{i-1}\sum_{b=1}^nxF(a,b)w[b][j] F(i,j)=∑a=0i−1∑b=1nxF(a,b)w[b][j]
= ∑ b = 1 n ( ∑ a = 0 i − 1 F ( a , b ) ) w [ b ] [ j ] x =\sum_{b=1}^n(\sum_{a=0}^{i-1}F(a,b))w[b][j]x =∑b=1n(∑a=0i−1F(a,b))w[b][j]x
设 S ( i , b ) = ∑ a = 0 i F ( a , b ) S(i,b)=\sum_{a=0}^{i}F(a,b) S(i,b)=∑a=0iF(a,b)
F ( i , j ) = ∑ b = 1 n S ( i − 1 , b ) w [ b ] [ j ] x F(i,j)=\sum_{b=1}^nS(i-1,b)w[b][j]x F(i,j)=∑b=1nS(i−1,b)w[b][j]x
S ( i , j ) − S ( i − 1 , j ) = ∑ b = 1 n S ( i − 1 , b ) w [ b ] [ j ] x S(i,j)-S(i-1,j)=\sum_{b=1}^nS(i-1,b)w[b][j]x S(i,j)−S(i−1,j)=∑b=1nS(i−1,b)w[b][j]x
式子化到这里如果不是 S S S是多项式,我甚至想要矩乘!
那就试一试呗!
构造矩阵
[ 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 ] \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 ] ⎣⎡w[1][1]x+1w[2][1]xw[3][1]xw[1][2]xw[2][2]x+1w[3][2]xw[1][3]xw[2][3]xw[3][3]x+1⎦⎤
可是多项式矩阵乘个鬼?
这个时候有一种神奇的操作,就是咱知道 F ( L , i ) F(L,i) F(L,i)是 L L L次多项式,所以咱们可以把 x x x带点值进去矩乘,然后再插回来。
带一个点值的复杂度是 O ( n 3 l o g L ) O(n^3logL) O(n3logL)的,带点值的复杂度是 O ( L n 3 L o g L ) O(Ln^3LogL) O(Ln3LogL),如果然后拉格朗日插值用 F F T FFT FFT优化的复杂度是 O ( L l o g 2 L ) O(Llog^2L) O(Llog2L),似乎蛮优秀的。。。。
在没看数据范围的情况下T-T
这个时候就得机智一点了,我们发现实际上我们仅仅需要知道 m o d    x k \mod x^k modxk意义下的多项式。
而且题目给了个条件 k ∣ p − 1 k|p-1 k∣p−1
那不是明白着让你带原根进去嘛,假设原根为 g g g,我们取 w = g p − 1 k w=g^{\frac{p-1}{k}} w=gkp−1,并带入 x = w 0 , w 1 ⋯ w k x=w^0,w^1\cdots w^k x=w0,w1⋯wk,这样的话次幂在带入的时候就自动循环了,就只需要带 k k k个值,带点值的部分变成了 k n 3 l o g L kn^3logL kn3logL。
简单地说,我们用带入的方式对 F ( L , i ) F(L,i) F(L,i)进行了一次 D F T DFT DFT
那么怎么求系数?根据 F F T FFT FFT的结论,只需要 I D F T IDFT IDFT一次出来的点值就是系数
也就是已知 F ( i ) = ∑ i = 0 k − 1 a i x i F(i)=\sum_{i=0}^{k-1}a_ix^i F(i)=∑i=0k−1aixi和 w w w,求
G ( i ) = ∑ i = 0 k − 1 1 k F ( w − i ) x i = 1 k ∑ i = 0 k − 1 ∑ j = 0 k − 1 w − i j a j G(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 G(i)=∑i=0k−1k1F(w−i)xi=k1∑i=0k−1∑j=0k−1w−ijaj
然后关于卷积中 i j ij ij项的处理,有一种操作是:
i j = ( i + j ) 2 − i 2 − j 2 2 ij=\frac{(i+j)^2-i^2-j^2}{2} ij=2(i+j)2−i2−j2
但是有点小问题,就是这道题如果有一个 1 2 \frac{1}{2} 21的话,可能找不到二次剩余。
另一种巧妙的转化方式是 i j = C i + j 2 − C i 2 − C j 2 ij=C_{i+j}^2-C_i^2-C_j^2 ij=Ci+j2−Ci2−Cj2
这个转化可以从组合意义的角度考虑。
这样的话就有
G ( i ) = 1 k ∑ i , j w C i 2 + C j 2 − C i + j 2 a j = 1 k w C i 2 ∑ i , j w − C i + j 2 w C j 2 a j G(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 G(i)=k1∑i,jwCi2+Cj2−Ci+j2aj=k1wCi2∑i,jw−Ci+j2wCj2aj
根据套路设 f i = w C i 2 a i , g i = w − C i 2 f_i=w^{C_i^2}a_i,g_i=w^{-C_{i}^2} fi=wCi2ai,gi=w−Ci2
把 g g g倍增反转卷积即可。
事实上,上述描述的算法流程就是 B l u e s t e i n ′ s A l g o r i t h m Bluestein's Algorithm Bluestein′sAlgorithm,一种用卷积实现任意长度 D F T DFT DFT和 I D F T IDFT IDFT的方法。
MTT好烦啊
#include
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;
}