題解 Gym100526I 【Interesting Integers】
題目大意
定義一種 \(Gabonacci\) 數列:
\[\begin{array}{c} G_1=a\\ G_2=b\\ G_i=G_{i-1}+G{i-2} \end{array} \]
給定一個正整數 \(n\) ,求最小的 \(a\) , \(b\) 使得 \(n\) 是該數列上的一個數。 \(2\le n\le 10^9\)
題解
看到這道題,首先的想法是可以先與處理出類似於矩陣快速冪一樣的東西,使得我們可以在 \(O(1)\) 的時間內算出對於一種 \(a\) 與 \(b\) 在 \(x\) 位之後的數字。因為 \(Fibonacc\) 數列的第 \(50\) 位早已超過了 \(10^9\)
作者這裡直接利用了矩陣乘法,預處理出前 \(40\) 位的矩陣。而這裡的 \(Gabonacci\) 數列和 \(Fibonacc\) 數列的狀態轉移矩陣是完全一模一樣的,應該沒人不知道吧,給大家看一下:
\[\begin{bmatrix} 1&1 \\ 1&0 \end{bmatrix} \]
而初始矩陣就易得為:
\[\begin{bmatrix} b&a \end{bmatrix} \]
即:
\[\begin{bmatrix} G_{i-1}&G_{i-2} \end{bmatrix} * \begin{bmatrix} 1&1 \\ 1&0 \end{bmatrix} = \begin{bmatrix} G_i&G_{i-1} \end{bmatrix} \]
然後我們考慮每一個預處理好的矩陣,我們如何通過狀態轉移矩陣和最後的值來求出初始的矩陣呢?我們可以回顧矩陣乘法,發現對於某一個轉移矩陣:
\[\begin{bmatrix} x&y \\ y&z \end{bmatrix} \]
\[G_i=a*x+b*y \]
這不就是求一個不定方程的特殊解嗎?我們可以利用擴充套件歐幾里得,計算出這個解。
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define int long long const int MAXN=45; struct Matrix { int n,m,h[3][3]; Matrix() { n=m=0; memset(h,0,sizeof(h)); } void Set1(const int x) { n=m=x; for(int i=1;i<=x;++i) h[i][i]=1; } }; Matrix operator * (const Matrix a,const Matrix b) { Matrix ans; ans.n=a.n; ans.m=b.m; for(int i=1;i<=a.n;++i) { for(int j=1;j<=b.m;++j) { for(int k=1;k<=a.m;++k) ans.h[i][j]+=a.h[i][k]*b.h[k][j]; } } return ans; } int t,n; Matrix stp[MAXN],tmp,ans; int l,r,mid,tag; int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } int lcm(int a,int b) { return a*b/gcd(a,b); } void exgcd(int a,int b,int &x,int &y,int z) { if(b==0) { x=z; y=0; return ; } exgcd(b,a%b,x,y,z); int tmp=x; x=y; y=tmp-a/b*y; return ; } signed main() { stp[1].n=stp[1].m=2; stp[1].h[1][1]=stp[1].h[1][2]=stp[1].h[2][1]=1; for(int i=2;i<=MAXN-5;++i) stp[i]=stp[i-1]*stp[1]; tmp.n=1; tmp.m=2; cin>>t; while(t--) { cin>>n; for(int i=MAXN-5;i>=1;--i) { int a=stp[i].h[1][1],b=stp[i].h[2][1]; int x,y,z=gcd(a,b); if(n%z!=0) continue; exgcd(a,b,x,y,z); x*=n/z; y*=n/z; int addx=lcm(a,b)/a; int addy=lcm(a,b)/b; int k=abs(x-y)/(addx+addy); if(x<y) { if(x+addx*k<y-addy*k) ++k; x+=addx*k; y-=addy*k; } else { if(x-addx*k<y+addy*k) --k; x-=addx*k; y+=addy*k; } if(x<=0||y<=0) continue; printf("%lld %lld\n",y,x); break; } } return 0; }