杜教BM(线性求递推答案)

简介

对于线性递推式,都是有规律的,不管是斐波那契形式的还是加入了其他运算,如果能找到递推规律可以直接写

否则,一种简便且行之有效的方法是:先求出前 T T T项(不放心可以求 1 e 6 1e6 1e6项)然后套用杜教BM板子直接求出结果

时间复杂度 O ( T ) O(T) O(T),有翻车的可能性

模板

#define rep(i,a,n) for (int i=a;i<n;i++)
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
const int N=?;  //传入多少项这里设置


ll powMOD(ll a, ll b) {
    ll ans = 1;
    for (; b; b >>= 1, a = a * a%Mod)if (b & 1)ans = ans * a%Mod;
    return ans;
}

namespace linear_seq {
    ll res[N], base[N], _c[N], _md[N];

    vector<int> Md;
    
    void mul(ll *a, ll *b, int k) {
        rep(i, 0, k + k) _c[i] = 0;
        rep(i, 0, k) if (a[i]) rep(j, 0, k) _c[i + j] = (_c[i + j] + a[i] * b[j]) % Mod;
        for (int i = k + k - 1; i >= k; i--) if (_c[i])
                rep(j, 0, SZ(Md)) _c[i - k + Md[j]] = (_c[i - k + Md[j]] - _c[i] * _md[Md[j]]) % Mod;
        rep(i, 0, k) a[i] = _c[i];
    }
    
    int solve(ll n, VI a, VI b) {
        ll ans = 0, pnt = 0;
        int k = SZ(a);
        assert(SZ(a) == SZ(b));
        rep(i, 0, k) _md[k - 1 - i] = -a[i]; _md[k] = 1;
        Md.clear();
        rep(i, 0, k) if (_md[i] != 0) Md.push_back(i);
        rep(i, 0, k) res[i] = base[i] = 0;
        res[0] = 1;
        while ((1ll << pnt) <= n) pnt++;
        for (int p = pnt; p >= 0; p--) {
            mul(res, res, k);
            if ((n >> p) & 1) {
                for (int i = k - 1; i >= 0; i--) res[i + 1] = res[i]; res[0] = 0;
                rep(j, 0, SZ(Md)) res[Md[j]] = (res[Md[j]] - res[k] * _md[Md[j]]) % Mod;
            }
        }
        rep(i, 0, k) ans = (ans + res[i] * b[i]) % Mod;
        if (ans < 0) ans += Mod;
        return ans;
    }
    
    VI BM(VI s) {
        VI C(1, 1), B(1, 1);
        int L = 0, m = 1, b = 1;
        rep(n, 0, SZ(s)) {
            ll d = 0;
            rep(i, 0, L + 1) d = (d + (ll)C[i] * s[n - i]) % Mod;
            if (d == 0) ++m;
            else if (2 * L <= n) {
                VI T = C;
                ll c = Mod - d * powMOD(b, Mod - 2) % Mod;
                while (SZ(C) < SZ(B) + m) C.pb(0);
                rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % Mod;
                L = n + 1 - L; B = T; b = d; m = 1;
            }
            else {
                ll c = Mod - d * powMOD(b, Mod - 2) % Mod;
                while (SZ(C) < SZ(B) + m) C.pb(0);
                rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % Mod;
                ++m;
            }
        }
        return C;
    }
    
    int gao(VI& a, ll n) {  //调用这个函数,传入打表前几项的vector,返回答案(默认对int数取模答案为int)
        VI c = BM(a);
        c.erase(c.begin());
        rep(i, 0, SZ(c)) c[i] = (Mod - c[i]) % Mod;
        return solve(n, c, VI(a.begin(), a.begin() + SZ(c)));
    }
};

例题

题目来自牛客“科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛

∑ i = 1 n i k f i b ( i ) , f i b ( i ) = f i b ( i − 1 ) + f i b ( i − 2 ) \sum_{i=1}^ni^kfib(i),fib(i)=fib(i-1)+fib(i-2) i=1nikfib(i),fib(i)=fib(i1)+fib(i2)

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 998244353;
const int maxn = 2e5 + 10;
const int N=1e5+10;

//此处省略了BM板子

ll f[maxn];

void init(){
    f[1]=f[2]=1;
    for(int i=3;i<N;i++)
        f[i]=(f[i-1]+f[i-2])%Mod;
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    ll n,k;
    init();
    cin>>n>>k;
    vector<int> g(N);
    g[0]=0;
    for(int i=1;i<N;i++)
        g[i]=(g[i-1]+powMOD(i,k)*f[i]%Mod)%Mod;
    int ans=linear_seq::gao(g,n);
    cout<<ans<<"\n";
    return 0;
}

你可能感兴趣的:(牛客比赛)