杜教BM递推板子

Berlekamp-Massey 算法用于求解常系数线性递推式

#include<bits/stdc++.h>

typedef std::vector<int> VI;
typedef long long ll;
typedef std::pair<int, int> PII;

const ll mod = 1000000007;

ll powmod(ll a, ll b) {
  ll res = 1;
  a %= mod;
  assert(b >= 0);
  for(; b; b >>= 1) {
    if(b & 1)
      res = res * a % mod;
    a = a * a % mod;
  }
  return res;
}

ll _, n;

namespace linear_seq {
const int N = 10010;
ll res[N], base[N], _c[N], _md[N];
std::vector<ll> Md;
void mul(ll *a, ll *b, int k) {
  for (int i = 0; i < k + k; i++)
    _c[i] = 0;
  for (int i = 0; i < k; i++)
    if (a[i])
      for (int j = 0; j < k; j++)
        _c[i + j] = (_c[i + j] + a[i] * b[j]) % mod;
  for (int i = k + k - 1; i >= k; i--)
    if (_c[i])
      for (int j = 0; j < ((int)(Md).size()); j++)
        _c[i - k + Md[j]] = (_c[i - k + Md[j]] - _c[i] * _md[Md[j]]) % mod;
  for (int i = 0; i < k; i++)
    a[i] = _c[i];
}
int solve(ll n, VI a, VI b) {
  ll ans = 0, pnt = 0;
  int k = ((int)(a).size());
  assert(((int)(a).size()) == ((int)(b).size()));
  for (int i = 0; i < k; i++)
    _md[k - 1 - i] = -a[i];
  _md[k] = 1;
  Md.clear();
  for (int i = 0; i < k; i++)
    if (_md[i] != 0)
      Md.push_back(i);
  for (int i = 0; i < k; i++)
    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;
      for (int j = 0; j < ((int)(Md).size()); j++)
        res[Md[j]] = (res[Md[j]] - res[k] * _md[Md[j]]) % mod;
    }
  }
  for (int i = 0; i < k; i++)
    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;
  for (int n = 0; n < ((int)(s).size()); n++) {
    ll d = 0;
    for (int i = 0; i < L + 1; i++)
      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 (((int)(C).size()) < ((int)(B).size()) + m)
        C.push_back(0);
      for (int i = 0; i < ((int)(B).size()); i++)
        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 (((int)(C).size()) < ((int)(B).size()) + m)
        C.push_back(0);
      for (int i = 0; i < ((int)(B).size()); i++)
        C[i + m] = (C[i + m] + c * B[i]) % mod;
      ++m;
    }
  }
  return C;
}
int gao(VI a, ll n) {
  VI c = BM(a);
  c.erase(c.begin());
  for (int i = 0; i < ((int)(c).size()); i++)
    c[i] = (mod - c[i]) % mod;
  return solve(n, c, VI(a.begin(), a.begin() + ((int)(c).size())));
}
};
int main() {
  int t;
  scanf("%d", &t);
  while(t--) {
    scanf("%lld", &n);
    std::vector<int>v = {
      1, 1, 0, 3, 0, 3,
      5, 4, 1, 9, 1, 6,
      9, 7, 2, 15, 2, 9,
      13, 10, 3, 21, 3, 12
    };
    //至少8项,越多越好。
    printf("%lld\n", linear_seq::gao(v, n - 1) % mod);
  }
}

数据大时都改为 long long
若mod不为质数,则需替换 powmod ,并将BM中的 d*powmod(b, mod-2) 改为 powmod(d, b)


void exgcd(ll a, ll b, ll &x, ll &y) {
  if (!b)
    x = 1, y = 0;
  else
    exgcd(b, a % b, y, x), y -= x * (a / b);
}
ll inv(ll a, ll p) {
  ll x, y;
  exgcd(a, p, x, y);
  return(x + p) % p;
}
VI prime, g;
void getPrime() {
  ll qw = mod;
  for(ll i = 2; i * i <= qw; i++) {
    if(qw % i == 0)
      prime.pb(i);
    while(qw % i == 0)
      qw /= i;
  }
  if(qw > 1)
    prime.push_back(qw);
}
ll powmod(ll fz, ll fm) {
  ll ret = 1ll;
  ll cnt[5] = {0};
  for(int k = 0; k < prime.size(); k++) {
    if(fz % prime[k] == 0) {
      while(fz % prime[k] == 0) {
        fz /= prime[k];
        cnt[k]++;
      }
    }
  }
  ret = fz % mod;
  for(int k = 0; k < prime.size(); k++) {
    if(fm % prime[k] == 0) {
      while(fm % prime[k] == 0) {
        fm /= prime[k];
        cnt[k]--;
      }
    }
  }
  if(fm > 1)
    ret = (ret * inv(fm, mod)) % mod;
  for(int k = 0; k < prime.size(); k++)
    for(int kk = 1; kk <= cnt[k]; kk++)
      ret = (ret * prime[k]) % mod;
  return ret;
}

原文地址:https://www.cnblogs.com/Forgenvueory/p/11371166.html

时间: 2024-10-29 03:24:44

杜教BM递推板子的相关文章

BM-线性递推板子

//杜教BM #include<bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se

BM递推杜教版

#include <bits/stdc++.h> using namespace std; #define rep(i,a,n) for (long long i=a;i<n;i++) #define per(i,a,n) for (long long i=n-1;i>=a;i--) #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #def

BM递推杜教版【扩展】

也就是模数不是质数的时候, //下面的板子能求质数和非质数,只需要传不同的参数. #include <cstdio> #include <cstdlib> #include <cassert> #include <cstring> #include <bitset> #include <cmath> #include <cctype> #include <unordered_map> #include <

杜教BM模板

#include<bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second

HDU6198 number number number(杜教BM模板)

给出一个数k,问用k个斐波那契数相加,得不到的数最小是几. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <string> #include <map> #include <set> #include <cassert> #include<

ACM-ICPC 2018 焦作赛区网络预赛 L:Poor God Water(杜教BM)

有N个小时,有三种食物(用a ,b ,c代替好了),每个小时要吃一种食物,要求任意连续三个小时不能出现aaa,bbb,ccc,abc,cba,bab,bcb(假设b为巧克力) 的方案数 先用矩阵打表 先对两个数统计有1.aa 2.bb 3.cc 4.ab 5.ba 6.ac 7.ca 8.bc 9.cb 在添加一个数,则会有 若ba后面只能添加a或c 即形成新的两个字母组合1.aa 6.ac 即5可以产生1和6(以下代码不是照上面数字敲的) 以此类推即可产生新的a数组 ll a[2][15];

BM递推

从别的大佬处看到的模板 #include<bits/stdc++.h> #define fi first #define se second #define INF 0x3f3f3f3f #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define pqueue priority_queue #define NEW(a,b) memset(a,b,sizeof(a)) #define Pii pair<int

杜教BM

#include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <iomanip> #include <cstdlib> #include <cstdio> #include <string> #include <vector> #include <bitset> #incl

51nod 1220 约数之和(杜教筛 + 推推推推推公式)

题意 给出\(n(1\leq n \leq 10^9)\),求\(\sum_{i=1}^n\sum_{j=1}^n\sigma(ij)\),其中\(\sigma(n)\)表示\(n\)的约数之和. balabala 交了两道杜教筛的的板子题(51nod 1239, 1244)就看到了这题,然后不会搞,然后看题解看了一天一夜终于彻底搞明白一发A掉了...感觉学到了很多,写个博客整理一下,如有错请指出. 技能需求 数论函数与线性筛 莫比乌斯反演(也可以当成容斥去理解) 狄利克雷卷积 杜教筛 强大的数