Linear homogeneous recurrence quick study notes seeking first n items

definition

If the number of the column \ (\ {a_i \} \ ) satisfies \ (= A_N \ sum_ = {I}. 1 ^ kf_i \ A_ {Ni} Times \) , then the number of order k as a linear recursion Sequence

Polynomial can use the knowledge to do \ (O (k \ log k \ log n) \) seeking Item n.

If k is given before entry, wondering \ (F_i \) , can (O (k ^ 2) \ ) \ obtained in time.

Seeking \ (f_i \)

There Berlekamp Massey algorithm and Reeds Sloane algorithm, specific algorithm idea is what we do not know, we just know this stuff into the opinions run.

The former needs to meet a number within each non-zero elements have multiplicative inverse, the latter do not.

Seeking \ (a_n \)

Impressions of the matrix multiplication. Suppose configured \ (k \ k CDOT \) square matrix A by the moment of the transfer matrix, the column vector composed of the first k entries of y (top to bottom are \ (a_0, a_1 ... a_ { k- 1} \) ). Our actual requirements \ (A_N = (A ^ ny) _0 \) .

Noting \ (A_N \) before items can be accumulated and written as k multiplied by a factor of the form, in other words, there are several columns \ (C_i \) such that \ (a_n = \ sum_ {i = 0} ^ {k-1} c_i \ Times a_i \) . Our goal is to find \ (C_i \) .

\(a_n=(A^ny)_0\) 代入 \(a_n=\sum_{i=0}^{k-1}c_ia_i\) ,得 \((A^ny)_0=\sum_{i=0}^{k-1}c_i \times a_i=\sum_{i=0}^{k-1}c_i \times (A^iy)_0\)

The subscript can in fact be removed, i.e. \ (A ^ NY = (\ sum_ I = {0} ^ {K}. 1-C_i \ Times A ^ I) Y \) .

A characteristic equation of the demand at that \ (f (\ lambda) = \ det | \ lambda E - A | = \ lambda ^ k-f_1 \ lambda ^ {k-1} -f_2 \ lambda ^ {k-2} F_k -...- \) . The Cayley-Hamilton theorem, there \ (F (A) = 0 \) .

Configuration polynomial \ (F (x) = x ^ n, G (x) = x ^ k-f_1x ^ {k-1} -f_2x ^ {k-2} -...- f_k, H (x) = \ I = {0} sum_ ^ {K}. 1 C_i X-I ^ \) . F is a polynomial of order n notes, H is k-1 order polynomial. Knowledge known by a polynomial, there must be a polynomial \ (the I (X) \) , so \ (the GI + H = F. \) . That \ (H = F. \ BMOD G \) . So we get a \ (C_i \) . The Cayley-Hamilton Theorem known, \ (F. (A) = 0I (A) + H (A) = H (A) \) , then the \ (F. (A) Y = H (A) Y \) , to ensure that the correctness.

Calculation \ (F. \ BMOD G \) , may be calculated \ (X ^ n-\ BMOD G \) , i.e., fast power + polynomial multiplying + polynomials modulo complexity \ (O (k \ log k \ log n) \)

Code

Berlekamp Massey algorithm and Reeds Sloane algorithm:

nowcoder889A

// given first m items init[0..m-1] and coefficents trans[0..m-1] or
// given first 2 *m items init[0..2m-1], it will compute trans[0..m-1]
// for you. trans[0..m] should be given as that
//      init[m] = sum_{i=0}^{m-1} init[i] * trans[i]
struct LinearRecurrence
{
    using int64 = long long;
    using vec = std::vector<int64>;
 
    static void extand(vec& a, size_t d, int64 value = 0)
    {
        if (d <= a.size()) return;
        a.resize(d, value);
    }
    static vec BerlekampMassey(const vec& s, int64 mod)
    {
        std::function<int64(int64)> inverse = [&](int64 a) {
            return a == 1 ? 1 : (int64)(mod - mod / a) * inverse(mod % a) % mod;
        };
        vec A = {1}, B = {1};
        int64 b = s[0];
        for (size_t i = 1, m = 1; i < s.size(); ++i, m++)
        {
            int64 d = 0;
            for (size_t j = 0; j < A.size(); ++j)
            {
                d += A[j] * s[i - j] % mod;
            }
            if (!(d %= mod)) continue;
            if (2 * (A.size() - 1) <= i)
            {
                auto temp = A;
                extand(A, B.size() + m);
                int64 coef = d * inverse(b) % mod;
                for (size_t j = 0; j < B.size(); ++j)
                {
                    A[j + m] -= coef * B[j] % mod;
                    if (A[j + m] < 0) A[j + m] += mod;
                }
                B = temp, b = d, m = 0;
            }
            else
            {
                extand(A, B.size() + m);
                int64 coef = d * inverse(b) % mod;
                for (size_t j = 0; j < B.size(); ++j)
                {
                    A[j + m] -= coef * B[j] % mod;
                    if (A[j + m] < 0) A[j + m] += mod;
                }
            }
        }
        return A;
    }
    static void exgcd(int64 a, int64 b, int64& g, int64& x, int64& y)
    {
        if (!b)
            x = 1, y = 0, g = a;
        else
        {
            exgcd(b, a % b, g, y, x);
            y -= x * (a / b);
        }
    }
    static int64 crt(const vec& c, const vec& m)
    {
        int n = c.size();
        int64 M = 1, ans = 0;
        for (int i = 0; i < n; ++i) M *= m[i];
        for (int i = 0; i < n; ++i)
        {
            int64 x, y, g, tm = M / m[i];
            exgcd(tm, m[i], g, x, y);
            ans = (ans + tm * x * c[i] % M) % M;
        }
        return (ans + M) % M;
    }
    static vec ReedsSloane(const vec& s, int64 mod)
    {
        auto inverse = [](int64 a, int64 m) {
            int64 d, x, y;
            exgcd(a, m, d, x, y);
            return d == 1 ? (x % m + m) % m : -1;
        };
        auto L = [](const vec& a, const vec& b) {
            int da = (a.size() > 1 || (a.size() == 1 && a[0])) ? a.size() - 1 : -1000;
            int db = (b.size() > 1 || (b.size() == 1 && b[0])) ? b.size() - 1 : -1000;
            return std::max(da, db + 1);
        };
        auto prime_power = [&](const vec& s, int64 mod, int64 p, int64 e) {
            // linear feedback shift register mod p^e, p is prime
            std::vector<vec> a(e), b(e), an(e), bn(e), ao(e), bo(e);
            vec t(e), u(e), r(e), to(e, 1), uo(e), pw(e + 1);
            ;
            pw[0] = 1;
            for (int i = pw[0] = 1; i <= e; ++i) pw[i] = pw[i - 1] * p;
            for (int64 i = 0; i < e; ++i)
            {
                a[i] = {pw[i]}, an[i] = {pw[i]};
                b[i] = {0}, bn[i] = {s[0] * pw[i] % mod};
                t[i] = s[0] * pw[i] % mod;
                if (t[i] == 0)
                {
                    t[i] = 1, u[i] = e;
                }
                else
                {
                    for (u[i] = 0; t[i] % p == 0; t[i] /= p, ++u[i])
                        ;
                }
            }
            for (size_t k = 1; k < s.size(); ++k)
            {
                for (int g = 0; g < e; ++g)
                {
                    if (L(an[g], bn[g]) > L(a[g], b[g]))
                    {
                        ao[g] = a[e - 1 - u[g]];
                        bo[g] = b[e - 1 - u[g]];
                        to[g] = t[e - 1 - u[g]];
                        uo[g] = u[e - 1 - u[g]];
                        r[g] = k - 1;
                    }
                }
                a = an, b = bn;
                for (int o = 0; o < e; ++o)
                {
                    int64 d = 0;
                    for (size_t i = 0; i < a[o].size() && i <= k; ++i)
                    {
                        d = (d + a[o][i] * s[k - i]) % mod;
                    }
                    if (d == 0)
                    {
                        t[o] = 1, u[o] = e;
                    }
                    else
                    {
                        for (u[o] = 0, t[o] = d; t[o] % p == 0; t[o] /= p, ++u[o])
                            ;
                        int g = e - 1 - u[o];
                        if (L(a[g], b[g]) == 0)
                        {
                            extand(bn[o], k + 1);
                            bn[o][k] = (bn[o][k] + d) % mod;
                        }
                        else
                        {
                            int64 coef = t[o] * inverse(to[g], mod) % mod * pw[u[o] - uo[g]] % mod;
                            int m = k - r[g];
                            extand(an[o], ao[g].size() + m);
                            extand(bn[o], bo[g].size() + m);
                            for (size_t i = 0; i < ao[g].size(); ++i)
                            {
                                an[o][i + m] -= coef * ao[g][i] % mod;
                                if (an[o][i + m] < 0) an[o][i + m] += mod;
                            }
                            while (an[o].size() && an[o].back() == 0) an[o].pop_back();
                            for (size_t i = 0; i < bo[g].size(); ++i)
                            {
                                bn[o][i + m] -= coef * bo[g][i] % mod;
                                if (bn[o][i + m] < 0) bn[o][i + m] -= mod;
                            }
                            while (bn[o].size() && bn[o].back() == 0) bn[o].pop_back();
                        }
                    }
                }
            }
            return std::make_pair(an[0], bn[0]);
        };
 
        std::vector<std::tuple<int64, int64, int>> fac;
        for (int64 i = 2; i * i <= mod; ++i)
        {
            if (mod % i == 0)
            {
                int64 cnt = 0, pw = 1;
                while (mod % i == 0) mod /= i, ++cnt, pw *= i;
                fac.emplace_back(pw, i, cnt);
            }
        }
        if (mod > 1) fac.emplace_back(mod, mod, 1);
        std::vector<vec> as;
        size_t n = 0;
        for (auto&& x : fac)
        {
            int64 mod, p, e;
            vec a, b;
            std::tie(mod, p, e) = x;
            auto ss = s;
            for (auto&& x : ss) x %= mod;
            std::tie(a, b) = prime_power(ss, mod, p, e);
            as.emplace_back(a);
            n = std::max(n, a.size());
        }
        vec a(n), c(as.size()), m(as.size());
        for (size_t i = 0; i < n; ++i)
        {
            for (size_t j = 0; j < as.size(); ++j)
            {
                m[j] = std::get<0>(fac[j]);
                c[j] = i < as[j].size() ? as[j][i] : 0;
            }
            a[i] = crt(c, m);
        }
        return a;
    }
 
    LinearRecurrence(const vec& s, const vec& c, int64 mod) : init(s), trans(c), mod(mod), m(s.size()) {}
    LinearRecurrence(const vec& s, int64 mod, bool is_prime = true) : mod(mod)
    {
        vec A;
        if (is_prime)
            A = BerlekampMassey(s, mod);
        else
            A = ReedsSloane(s, mod);
        if (A.empty()) A = {0};
        m = A.size() - 1;
        trans.resize(m);
        for (int i = 0; i < m; ++i)
        {
            trans[i] = (mod - A[i + 1]) % mod;
        }
        std::reverse(trans.begin(), trans.end());
        init = {s.begin(), s.begin() + m};
    }
    int64 calc(int64 n)
    {
        if (mod == 1) return 0;
        if (n < m) return init[n];
        vec v(m), u(m << 1);
        int msk = !!n;
        for (int64 m = n; m > 1; m >>= 1) msk <<= 1;
        v[0] = 1 % mod;
        for (int x = 0; msk; msk >>= 1, x <<= 1)
        {
            std::fill_n(u.begin(), m * 2, 0);
            x |= !!(n & msk);
            if (x < m)
                u[x] = 1 % mod;
            else
            { // can be optimized by fft/ntt
                for (int i = 0; i < m; ++i)
                {
                    for (int j = 0, t = i + (x & 1); j < m; ++j, ++t)
                    {
                        u[t] = (u[t] + v[i] * v[j]) % mod;
                    }
                }
                for (int i = m * 2 - 1; i >= m; --i)
                {
                    for (int j = 0, t = i - m; j < m; ++j, ++t)
                    {
                        u[t] = (u[t] + trans[j] * u[i]) % mod;
                    }
                }
            }
            v = {u.begin(), u.begin() + m};
        }
        int64 ret = 0;
        for (int i = 0; i < m; ++i)
        {
            ret = (ret + v[i] * init[i]) % mod;
        }
        return ret;
    }
 
    vec init, trans;
    int64 mod;
    int m;
};

int n, m;
const int mod = 1e9;
ll calc(ll x, ll y) {
    ll z = 1;
    while (y){
        if (y & 1) (z *= x) %= mod;
        (x *= x) %= mod, y /= 2;
    }
    return z;
}
inline int update(int x) {
    return x < mod ? x : x - mod;
}
vector<long long>f, fm;
int main(){
    //freopen("input", "r", stdin);
    scanf("%d %d", &n, &m);
    f.push_back(0);
    f.push_back(1);
    for (int i = 2; i <= m + m ; i ++){
        f.push_back(update(f[i - 1] + f[i - 2]));
    }
    fm.push_back(0);
    for (int i = 1; i <= m; i ++)
        fm.push_back(update(fm[i - 1] + calc(f[i], m)));
    LinearRecurrence sol(fm, mod, false);
    printf("%d\n", sol.calc(n));
    return 0;
}

Rapid Evaluation Item linear recursive series n:

luoguP4723

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 998244353;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

LL ksm(LL a,LL b) {
    LL ans = 1;
    while(b) {
        if(b&1) ans = a * ans % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

struct NTTranform {
    const int g = 3;
    void Transform(int *a,int n,int opt) {
        for(int i = 0,j = 0;i < n;i ++) {
            if(i < j) swap(a[i],a[j]);
            for(int k = n >> 1;(j ^= k) < k;k >>= 1);
        }
        for(int l = 2;l <= n;l *= 2) {
            int m = l / 2;
            int wn = ksm(g,(mod-1)/l);
            if(opt == -1) wn = ksm(wn,mod - 2);
            for(int *p = a;p != a + n;p += l) {
                for(int i = 0,w = 1;i < m;i ++,w=1ll*w*wn%mod) {
                    int t = 1ll * w * p[m + i] % mod;
                    p[m + i] = (p[i] - t + mod) % mod;
                    (p[i] += t) %= mod;
                }
            }
        }
    }
    void dft(int *a,const int n) {
        Transform(a,n,1);
    }
    void idft(int *a,const int n) {
        Transform(a,n,-1);
        int t = ksm(n,mod - 2);
        for(int i = 0;i < n;i ++) a[i] = 1ll * a[i] * t % mod;
    }
}ntt;

void multiply(int *a,int n,int *b,int m,int *ans) { /// need 4 times memory
    static int c1[SZ],c2[SZ];
    int len = 1;
    while(len < n + m) len *= 2;
    for(int i = 0;i < len;i ++) c1[i] = c2[i] = 0;
    for(int i = 0;i < n;i ++) c1[i] = a[i];
    for(int i = 0;i < m;i ++) c2[i] = b[i];
    ntt.dft(c1,len); ntt.dft(c2,len);
    for(int i = 0;i < len;i ++) c1[i] = 1ll * c1[i] * c2[i] % mod;
    ntt.idft(c1,len);
    for(int i = 0;i < n + m - 1;i ++) ans[i] = (c1[i] + mod) % mod;
}

void inverse(int *a,int n,int *b) { /// need 4 times memory
    static int A[SZ];
    b[0] = ksm(a[0],mod-2);
    for(int l = 2;l < n*2;l <<= 1) {
        for(int i = 0;i < l;i ++) A[i] = a[i];
        for(int i = l;i < l*2;i ++) A[i] = 0;
        for(int i = l/2;i < l*2;i ++) b[i] = 0;
        ntt.dft(A,l*2); ntt.dft(b,l*2);
        for(int i = 0;i < l*2;i ++) b[i] = (b[i]*2-1ll*A[i]*b[i]%mod*b[i]%mod+mod)%mod;
        ntt.idft(b,l*2);
    }
}

void divide(int *a,int n,int *b,int m,int *D) { /// need 4 times memory
    static int bni[SZ];
    if(n<m) { D[0]=0; return ; }
    reverse(a,a+n); reverse(b,b+m);
    inverse(b,n-m+1,bni);
    multiply(a,n-m+1,bni,n-m+1,D);
    reverse(D,D+n-m+1);
    reverse(a,a+n); reverse(b,b+m);
}

void modular(int *a,int n,int *b,int m,int *R) { /// need 4 times memory
    static int D[SZ],c[SZ];
    if(n<m) { for(int i = 0;i < n;i ++) R[i] = a[i]; return ; }
    divide(a,n,b,m,D);
    multiply(b,m,D,n-m+1,c);
    for(int i = 0;i < m-1;i ++) R[i] = (a[i] - c[i] + mod) % mod;
    R[m-1] = 0;
}

// f:长度为k的线性递推式,a:初始k项。求第n项
int get_nth(int f[],int a[],int k,int n) {
    if(n<k) return a[n];
    static int ff[SZ],G[SZ],Ans[SZ];
    for(int i = 1;i <= k;i ++) ff[k-i] = (mod - f[i])%mod;
    ff[k] = 1;
    G[1] = 1; Ans[0] = 1;
    while(n) {
        if(n&1) {
            multiply(Ans,k,G,k,Ans),modular(Ans,2*k-1,ff,k+1,Ans);
        }
        multiply(G,k,G,k,G);
        modular(G,2*k-1,ff,k+1,G);
        n >>= 1;
    }
    int ans = 0;
    for(int i = 0;i < k;i ++) (ans += 1ll * a[i] * Ans[i] % mod) %= mod;
    ans += mod; ans %= mod;
    return ans;
}

int n, k;
int f[SZ],a[SZ];

int main(){
    //freopen("testdata (4).in","r",stdin);
    n = read(), k = read();
    for(int i = 1;i <= k;i ++) f[i] = read();
    for(int i = 0;i < k;i ++) a[i] = read();
    cout << get_nth(f,a,k,n);
    return 0;
}

Guess you like

Origin www.cnblogs.com/dqsssss/p/11374099.html