跳转至

矩阵乘法题单

P哥破解密码

定义一个串合法,当且仅当串只由 \(\verb!A!\)\(\verb!B!\) 构成,且没有连续的 \(3\)\(\verb!A!\)。P 哥知道,密码就是长度为 \(N\) 的合法字符串数量对 \(19260817\) 取模的结果。但是 P 哥不会算,所以他只能把 \(N\) 告诉你,让你来算。

至于为什么要对这个数取模,好像是因为纪念某个人,但到底是谁,P 哥也不记得了。

然而他忘记字符串长度 \(N\) 应该是多少了,于是他准备试 \(M\) 组数据。

输入格式

第一行给出一个整数 \(M\) 表示询问次数。

接下来 \(M\) 行每行给出一个正整数 \(N\),表示该组询问中字符串的长度。

输出格式

对于每一次询问输出一行一个整数表示答案。

  • 对于 \(100\%\) 数据,全部 \(N\leq10^9\)\(M\leq10\)

先写出朴素转移,发现有很强的阶段性(即i从i-1转移)。考虑矩阵乘法。

弄清楚矩阵乘法的对应关系,还是很简单的。构造转移矩阵即可。

将f数组映射到A的第一行,再使用快速幂解决\(A·B^k\)即可

// Problem: P4838 P哥破解密码
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4838
// Memory Limit: 500 MB
// Time Limit: 1000 ms
// Challenger: Erica N
// ----
// 
#include<bits/stdc++.h>

using namespace std;
#define rd read()
#define ull unsigned long long
#define int long long 
#define pb push_back
#define itn int
#define ps second 
#define pf first


#define rd read()
int read()
{
  int xx = 0, ff = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9')
  {
    if (ch == '-')
      ff = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9')
    xx = xx * 10 + (ch - '0'), ch = getchar();
  return xx * ff;
}
#define zerol = 1
#ifdef zerol
#define cdbg(x...) do { cerr << #x << " -> "; err(x); } while (0)
void err() {
    cerr << endl;
}
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x) {
    for (auto v: a) cerr << v << ' ';
    err(x...);
}
template<typename T, typename... A>
void err(T a, A... x) {
    cerr << a << ' ';
    err(x...);
}
#else
#define dbg(...)
#endif
const int N=1e7+5;
const ull P=137;
const int MOD=19260817;
const int INF=1e18+7;
/*

策略


*/  

int f[N][3];


struct math{
    int a[3][3];

    void init(){
        memset(a,0,sizeof a);
    }
    math operator *(const math b)const{
        math r;r.init();
        for(int k=0;k<3;k++){
            for(int i=0;i<3;i++){
                for(int j=0;j<3;j++){
                    (r.a[i][j]+=a[i][k]*b.a[k][j]%MOD)%=MOD;
                }
            }
        }
        return r;
    }
};


math ksm(math a,int b){
    math r=a;
    b--;
    while(b){
        if(b&1){
            r=r*a;
        }
        b>>=1;
        a=a*a;
    }

    cdbg(b);
    return r;
}


void solve(){
    int n=rd;
    math a,b;
    a.init();
    b.init();
    a.a[0][0]=1;
    // for(int i=1;i<=n;i++){
        // f[i][0]=(f[i-1][0]+f[i-1][1]+f[i-1][2])%MOD;
        // f[i][1]=f[i-1][0];
        // f[i][2]=f[i-1][1];
    // }

    b.a[0][0]=b.a[1][0]=b.a[2][0]=b.a[0][1]=b.a[1][2]=1;
    // for(int i=1;i<=n;i++){
        // a=a*b;
    // }
    a=a*ksm(b,n);
//  cdbg("OK");

    cout<<(a.a[0][0]+a.a[0][1]+a.a[0][2])%MOD<<endl;
}

signed main(){
    int t=rd;
    while(t--){
        solve();
    }
}

斐波那契公约数

题目描述

对于 Fibonacci 数列: \(f_i = \begin{cases} [i = 1] & i \leq 1 \\ f_{i - 1} + f_{i - 2} & i \gt 1 \end{cases}\)

请求出 \(f_n\)\(f_m\) 的最大公约数,即 \(\gcd(f_n, f_m)\)

  • 对于 \(100\%\) 的数据,保证 \(1 \leq n, m \leq 10^9\)

重要结论:gcd(F[n],F[m])=F[gcd(n,m)]

推导:

www.luogu.com.cn

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=105,MOD=1e8;
int n,K;
struct node {
    int a[5][5];
    node() {
        memset(a,0,sizeof a); //一定要写!
    }
};

node f(node a,node b) {
    node x;
    for(int K=1; K<=2; K++)
        for(int i=1; i<=2; i++) {
            for(int j=1; j<=2; j++)x.a[i][j]=(x.a[i][j]+a.a[i][K]*b.a[K][j]%MOD)%MOD;
        }
    return x;
}
node ksm() {
    node res,c;
    for(int i=1; i<=2; i++)res.a[i][i]=1;
    c.a[1][1]=c.a[1][2]=c.a[2][1]=1;
    while(K) {
        if(K&1) {
            res=f(res,c);
        }
        c=f(c,c);
        K>>=1;
    }
    return res;
}

signed main() {

    int n,m;
    cin>>n>>m;

    K=__gcd(n,m);
    if(K<=2){
        cout<<1<<endl;
        return 0;
    }
    node res;
    res=ksm();
    cout<<res.a[2][1]<<endl;
    return 0;
}

广义斐波那契

(矩阵快速幂的通用递推式推导|对于有初始值及类斐波那契数列的变种)

www.luogu.com.cn

P1349 广义斐波那契数列

广义的斐波那契数列是指形如 \(a_n=p\times a_{n-1}+q\times a_{n-2}\) 的数列。

今给定数列的两系数 \(p\)\(q\),以及数列的最前两项 \(a_1\) 和 $ a_2$,另给出两个整数 \(n\)\(m\),试求数列的第 \(n\)\(a_n\)\(m\) 取模后的结果。

输入包含一行六个整数,\(p,q,a_1,a_2,n,m\)

输出包含一行一个整数表示答案。

【数据范围】 对于 \(100\%\) 的数据,\(p,q,a_1,a_2 \in [0,2^{31}-1]\)\(1\le n,m \le 2^{31}-1\)


/*
Code by Ntsc_Hodaka
*/

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define pii pair<int, int>

///----///
#define rd read()
inline int read() {
  int xx = 0, ff = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-')
      ff = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9')
    xx = xx * 10 + (ch - '0'), ch = getchar();
  return xx * ff;
}
inline void write(int out) {
  if (out < 0)
    putchar('-'), out = -out;
  if (out > 9)
    write(out / 10);
  putchar(out % 10 + '0');
}

///----///
const int N = 2e2 + 5;
const int M = 1e7 + 5;
const int INF = 1e12 + 5;
int MOD = 998244353;
const double eps = 1e-7;

int n, p, q, a1, a2;
struct node {
  int m[10][10];
} ans, base;
inline void init() {
  ans.m[1][1] = a2, ans.m[1][2] = a1;
  base.m[1][1] = p, base.m[2][1] = q, base.m[1][2] = 1;
}
inline node mul(node a, node b) {
  node res;
  memset(res.m, 0, sizeof(res.m));
  for (int i = 1; i <= 2; i++) {
    for (int j = 1; j <= 2; j++) {
      for (int k = 1; k <= 2; k++) {
        res.m[i][j] += (a.m[i][k] % MOD) * (b.m[k][j] % MOD);
        res.m[i][j] %= MOD;
      }
    }
  }
  return res;
}
inline void ksm(int p) {
  while (p) {
    if (p & 1)
      ans = mul(ans, base);
    base = mul(base, base);
    p >>= 1;
  }
}

signed main() {
  p = rd, q = rd, a1 = rd, a2 = rd, n = rd, MOD = rd;
  if (n == 1) {
    cout << a1;
    return 0;
  }
  if (n == 2) {
    cout << a2;
    return 0;
  }
  init();
  ksm(n - 2);
  // cerr<<"OK";
  printf("%lld", ans.m[1][1] % MOD);
}