CF923E Perpetual Subtraction

https://www.luogu.com.cn/problem/CF923E

感谢这题让我学会了矩阵对角化 和 快速的矩阵快速幂

题目已经给了一个线性变换了,要你求它的 k k k次方
在这里插入图片描述

不难想到矩阵对角化

A k = Q ( Q − 1 A Q ) k Q − 1 A^k=Q(Q^{-1}AQ)^{k}Q^{-1} Ak=Q(Q1AQ)kQ1
中间那个 Q − 1 A Q Q^{-1}AQ Q1AQ是个对角矩阵, k k k次方直接 O ( n l o g k ) O(nlogk) O(nlogk)

通过之前的学习,可以发现 Q − 1 A Q Q^{-1}AQ Q1AQ对角线上的值就是矩阵的特征值
所以没有必要直接乘,把特征值全部求出来即可

具体的过程慢慢算吧,
在这里插入图片描述

然后我们把最下面那个设为 D D D
现在要求的就是
Q D k Q − 1 p ⃗ QD^kQ^{-1} \vec{p} QDkQ1p
考虑从右往左乘,向量乘矩阵,可以发现 Q − 1 和 Q Q^{-1}和Q Q1Q都是可以用NTT加速的
然后就做完了
code:

#include<bits/stdc++.h>
#define mod 998244353
#define ll long long
#define N 800050
using namespace std;
int add(int x, int y) { x += y;
    if(x >= mod) x -= mod;
    return x;
}
int sub(int x, int y) { x -= y;
    if(x < 0) x += mod;
    return x;
}
int mul(int x, int y) {
    return 1ll * x * y % mod;
}
int qpow(int x, int y) {
    int ret = 1;
    for(; y; y >>= 1, x = mul(x, x)) if(y & 1) ret = mul(ret, x);
    return ret;
}
const int G = 3;
const int Ginv = qpow(G, mod - 2);
int rev[N];
void ntt(int *a, int n, int o) {
    for(int i = 1; i < n; i ++) if(i > rev[i]) swap(a[i], a[rev[i]]);
    for(int len = 2; len <= n; len <<= 1) {
        int w0 = qpow(o == 1? G : Ginv, (mod - 1) / len);
        for(int j = 0; j < n; j += len) {
            int wn = 1;
            for(int k = j; k < j + (len >> 1); k ++, wn = mul(wn, w0)) {
                int X = a[k], Y = mul(wn, a[k + (len >> 1)]);
                a[k] = add(X, Y), a[k + (len >> 1)] = sub(X, Y);
            }
        }
    }
    int ninv = qpow(n, mod - 2);
    if(o == -1)
        for(int i = 0; i < n; i ++) a[i] = mul(a[i], ninv);
}
int a[N], b[N];
void Mul(int *f, int *g, int n, int m) {
    memset(a, 0, sizeof a), memset(b, 0, sizeof b);
    for(int i = 0; i <= n; i ++) a[i] = f[i];
    for(int i = 0; i <= m; i ++) b[i] = g[i];
    int len = 1;
    for(; len <= n + m; ) len <<= 1;
    for(int i = 1; i < len; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (len >> 1));
    ntt(a, len, 1), ntt(b, len, 1);
    for(int i = 0; i < len; i ++) a[i] = mul(a[i], b[i]);
    ntt(a, len, -1);
    for(int i = 0; i <= n; i ++) f[i] = a[i];
}
int fac[N], ifac[N], f[N], g[N], inv[N], n;
void init(int n) {
    fac[0] = 1;
    for(int i = 1; i <= n; i ++) fac[i] = mul(fac[i - 1], i);
    ifac[n] = qpow(fac[n], mod - 2);
    for(int i = n - 1; i >= 0; i --) ifac[i] = mul(ifac[i + 1], i + 1);
    inv[1] = 1;
    for(int i = 2; i <= n; i ++) inv[i] = mul((mod - mod / i), inv[mod % i]);
  //  printf("\n");;
 //   for(int i = 0; i <= n; i ++) printf(" %d ", mod - mod / i); printf("\n");
}
ll m;
int main() {
    scanf("%d%lld", &n, &m); m %= mod - 1;
    init(n * 2);
    for(int i = 0; i <= n; i ++) scanf("%d", &f[i]);
    for(int i = 0; i <= n; i ++) f[i] = mul(f[i], fac[i]), g[i] = ifac[i];
    
    
//    for(int i = 0; i <= n; i ++) printf("%d ", f[i]); printf("\n");
//    for(int i = 0; i <= n; i ++) printf("%d ", g[i]); printf("\n");
    
    reverse(f, f + 1 + n);
    Mul(f, g, n, n);
    reverse(f, f + 1 + n);
  //  for(int i = 0; i <= n; i ++) printf("%d ", f[i]); printf("  f\n");
    
    for(int i = 0; i <= n; i ++) f[i] = mul(f[i], qpow(inv[i + 1], m)), f[i] = mul(f[i], qpow(mod - 1, i));
    
  //  for(int i = 0; i <= n; i ++) printf("%d ", inv[i]); printf("  f\n");
    reverse(f, f + 1 + n);
    Mul(f, g, n, n);
    reverse(f, f + 1 + n);
    
    for(int i = 0; i <= n; i ++) f[i] = mul(f[i], ifac[i]), f[i] = mul(f[i], qpow(mod - 1, i));
    for(int i = 0; i <= n; i ++) printf("%d ", f[i]);
    return 0;
}
posted @ 2021-09-25 22:15  lahlah  阅读(172)  评论(0编辑  收藏  举报