1. 程式人生 > >【做題】CF177G2. Fibonacci Strings——思維+數列

【做題】CF177G2. Fibonacci Strings——思維+數列

while 所有 sub class fibonacci bre mat space bits

題意:定義斐波那契字符串為:

  • $f_1 = $ "a"
  • \(f_2 =\) "b"
  • \(f_n = f_{n-1} + f_{n-2}, \, n > 2\)

例如,$f_3 = $ “ba”。

\(m\)次詢問,第\(i\)次給出一個字符串\(s_i\),問\(s_i\)\(f_n\)中的出現次數。

\(m \leq 10^4, \, n \leq 10^{18}, \, \sum|s_i| \leq 10^5\)

主要問題在與\(f_p\)\(f_{p-1}\)拼接時,\(f_p\)的某個後綴與\(f_{p-1}\)的某個前綴可能恰好拼成\(s_i\)

,即產生額外的出現次數。當\(|f_p|\)\(|f_{p-1}|\)都大於等於\(|s_i|\)時,這個數值就等於\(f_{n}\)長度為\(|s_i|-1\)的後綴與\(f_{p-1}\)長度為\(|s_i|-1\)的前綴組成的字符串中\(s_i\)的出現次數。

我們設\(f_{p-1}\)長度為\(|s_i|-1\)的前綴為\(a\),長度為\(|s_i|-1\)的後綴為\(b\)\(f_{p}\)長度為\(|s_i|-1\)的前綴為\(a\),長度為\(|s_i|-1\)的後綴為\(c\)。我們觀察發現:

長度為\(|s_i|-1\)的前綴 長度為\(|s_i|-1\)的後綴 產生額外貢獻的字符串
\(f_{p-1}\) \(a\) \(b\)
\(f_p\) \(a\) \(c\) \(ca\)
\(f_{p+1}\) \(a\) \(b\) \(ba\)
\(f_{p+2}\) \(a\) \(c\) \(ca\)
\(f_{p+3}\) \(a\) \(b\) \(ba\)
…… …… …… ……
\(f_{p+2k}\) \(a\) \(c\) \(ca\)
\(f_{p+2k+1}\) \(a\) \(b\) \(ba\)

我們設\(s_i\)\(ca\)中的出現次數為\(n_c\),在\(ba\)中的出現次數為\(n_b\)

\(O_n\)表示\(s_i\)\(f_{n+p}\)中的出現次數。

那麽,容易得到

\[O_n = O_{n-1} + O_{n-2} + \begin{cases} n_c, & \text {if $ n\mod 2 = 0$} \\ n_b, & \text{if $n \mod 2 = 1$}\end{cases}\]

考慮拆分貢獻,即設\(A_n\)\(B_n\)\(C_n\)分別表示\(f_n\)中,\(s_i\)\(f_p\)\(f_{p+1}\)中的出現次數,在所有\(ba\)中的出現次數,在所有\(ca\)中的出現次數。那麽,我們有

  • \(O_n = A_n + B_n \times n_b + C_n \times n_c\)
  • \(A_n = A_{n-1} + A_{n-2}\)
  • \(B_n = B_{n-1} + B_{n-2} + [n \mod 2 = 1] = B_{n-1} + B_{n-2} + \frac {1 - (-1)^n} {2}\)
  • \(C_n = C_{n-1} + C_{n-2} + [n \mod 2 = 0] = C_{n-1} + C_{n-2} + \frac {1 + (-1)^n} {2}\)
  • \(B_0 = B_1 = C_0 = C_1 = 0\)

其中,\(A_n\)在我們計算出\(A_0\)\(A_1\)後,用矩陣快速冪得到。故我們只用考慮\(B_n\)\(C_n\)這兩個類似的數列。

通過使用OEIS或其他的數列求解方法,我們得到\(B_n = F_{n-1} - \frac {1+(-1)^n}{2}\),以及\(C_n = F_{n} - \frac{1 - (-1)^n}{2}\)。其中,\(F_n\)為第\(n\)個斐波那契數。它們同樣可以用矩陣快速冪求出。

時間復雜度\(O(\sum|s_i| + m \log n )\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 30010, MAX = 200000, MOD = 1000000007;
string fib[N];
int n,m,len,cnt,lef[N],nex[MAX + 10];
char tmp[MAX + 10];
string a,b,c,tp;
struct matrix {
  int mat[3][3];
  matrix() {
    memset(mat,0,sizeof mat);
  }
  matrix operator * (const matrix& x) const {
    matrix ret = matrix();
    for (int k = 1 ; k <= 2 ; ++ k)
      for (int i = 1 ; i <= 2 ; ++ i)
    for (int j = 1 ; j <= 2 ; ++ j)
      (ret.mat[i][j] += 1ll * mat[i][k] * x.mat[k][j] % MOD) %= MOD;
    return ret;
  }
};
matrix bas;
matrix power(matrix a,int b) {
  matrix ret = matrix();
  ret.mat[1][1] = ret.mat[2][2] = 1;
  while (b) {
    if (b&1) ret = ret * a;
    a = a * a;
    b >>= 1;
  }
  return ret;
}
int getfib(int x) {
  matrix ret = power(bas,x);
  return ret.mat[1][2];
}
int getnum() {
  int ret = 0;
  for (int i = 0, j = 0 ; i < (int)tp.length() ; ++ i) {
    while (j >= 0 && tmp[j+1] != tp[i])
      j = nex[j];
    ++ j;
    if (j == len) ++ ret, j = nex[j];
  }
  return ret;
}
int solve() {
  nex[0] = -1;
  for (int i = 2, j = 0 ; i <= len ; ++ i) {
    while (j >= 0 && tmp[j+1] != tmp[i])
      j = nex[j];
    nex[i] = ++j;
  }  
  int p = lower_bound(lef+1,lef+cnt+1,len) - lef;
  ++ p;
  if (n <= p+1) {
    tp = fib[n];
    return getnum();
  }
  a = fib[p].substr(0,len-1);
  b = fib[p].substr(lef[p] - len+1,len-1);
  c = fib[p+1].substr(lef[p+1] - len+1,len-1);
  int nb, nc, n0, n1, pos = n - p, ret = 0;
  tp = b + a;
  nb = getnum();
  tp = c + a;
  nc = getnum();
  tp = fib[p];
  n0 = getnum();
  tp = fib[p+1];
  n1 = getnum();
  (ret += 1ll * (getfib(pos) - (pos&1)) * nc % MOD) %= MOD;
  (ret += 1ll * (getfib(pos-1) - 1 + (pos&1)) * nb % MOD) %= MOD;
  matrix sta = matrix();
  sta.mat[1][1] = n1;
  sta.mat[1][2] = sta.mat[2][1] = n0;
  sta = sta * power(bas,pos);
  (ret += sta.mat[1][2]) %= MOD;
  ret = (ret % MOD + MOD) % MOD;
  return ret;
}
signed main() {
  fib[1] = "a";
  fib[2] = "b";
  for (int i = 3 ; ; ++ i) {
    fib[i] = fib[i-1] + fib[i-2];
    lef[i] = fib[i].length();
    cnt = i;
    if (lef[i-1] >= MAX) break;
  }
  bas.mat[1][1] = bas.mat[1][2] = bas.mat[2][1] = 1;
  cin >> n >> m;
  for (int i = 1 ; i <= m ; ++ i) {
    scanf("%s",tmp+1);
    len = strlen(tmp+1);
    cout << solve() << endl;
  }
  return 0;
}


小結:用一種不大簡單的做法做出了這道題。思考時間過長,並且依賴網站來求解數列,這是做此題時體現出的不足之處。

【做題】CF177G2. Fibonacci Strings——思維+數列