題目的大意就是求等差數列對應的Fibonacci數值的和,容易知道Fibonacci對應的矩陣爲[1,1,1,0],因爲題目中f[0]=0,f[1]=1,所以推出最後結果f[n]=(A^n-1).a,所以 f(g(i))= f(k*i+b)= (A^(k*i+b-1)).a,i從 0取到 n-1,取出公因式 A^(b-1)(因爲矩陣滿足分配率),然後所求結果可化爲 A^(b-1) * (A^0 + A^k + A^2k +....+ A^(n-1)k),化到這裏後難點就是求和了,一開始我嘗試暴力求和(每個A^k可以用快速冪求出,logn級別),即O(n)的做法,結果TLE了,預料之中,這時我竟傻乎乎地套用等比數列求和公式,即(A^nk -A^0) /(A^k -E),按比例放大再相減是沒錯,問題是不是簡單的相除……總之思路應該是錯的了,後來看別人的博客後才知道原來可以用二分來求和的,即 A^0 + A^k + A^2k +....+ A^(n-1)k = (A^0 + A^k + A^2k +...+ A^(n/2-1)k) *(E + A^(n/2)k),遞歸來求和,處理好 n的奇偶性即可,但我還是調試了好久,就因爲在一些細節問題上出錯卻檢查不出來,下面附上代碼:
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long LL;
LL mod= 1000000007; //初不初始化都沒問題,只是爲了防止忘記讀入時產生的異常退出
struct matrix{
LL a,b,c,d;
matrix(LL a=0, LL b=0, LL c=0, LL d=0): a(a),b(b),c(c),d(d) {}
matrix operator +(const matrix &m2){
return matrix((a+m2.a)%mod,(b+m2.b)%mod,(c+m2.c)%mod,(d+m2.d)%mod);
}
matrix operator *(const matrix &m2){
return matrix((a*m2.a%mod+b*m2.c%mod)%mod, (a*m2.b%mod+b*m2.d%mod)%mod, (c*m2.a%mod+d*m2.c%mod)%mod, (c*m2.b%mod+d*m2.d%mod)%mod);
}
//一開始等比數列求和的思路要用到除法,後來才發現是錯的,不過也不刪了,就放在這吧
matrix operator /(const matrix &m2){
//二維求逆很好求的
matrix inv= matrix(m2.d,-m2.b,-m2.c,m2.a);
LL tmp= m2.a*m2.d-m2.b*m2.c;
return matrix((a*inv.a/tmp%mod+b*inv.c/tmp%mod)%mod, (a*inv.b/tmp%mod+b*inv.d/tmp%mod)%mod, (c*inv.a/tmp%mod+d*inv.c/tmp%mod)%mod, (c*inv.b/tmp%mod+d*inv.d/tmp%mod)%mod);
}
};
// A爲 Fibonacci矩陣,E爲單位矩陣,設爲全局變量更方便一些
matrix A(1,1,1,0),E(1,0,0,1);
//簡單的快速冪
matrix quick_mod(matrix m, LL b){
if(b==-1) return matrix(0,1,1,-1);
//若指數爲-1,返回矩陣 A^-1,相當於A^1的逆(算了好久T.T)
matrix res(E); //res一開始爲單位矩陣
while(b){
if(b&1) res= res*m;
m= m*m;
b>>=1;
}
return res;
}
//二分法計算 A^0 + A^k + A^2k +....+ A^(n-1)k 的和
//即 sum =(A^0 + A^k + A^2k +...+ A^(n/2-1)k) *(E + A^(n/2)k),分治的思想
matrix quick_sum(LL k, LL n){
if(n==1) return E; //若 n爲1,即計算 A^0,此時返回的是 E!而不是 A!一開始沒想到錯在這裏,卡了好久 T.T
if(n%2==0) return quick_sum(k,n/2)*(E+quick_mod(A,(n/2)*k));
//else return quick_sum(k,(n-1)/2)*(E+quick_mod(A,((n-1)/2)*k))*quick_mod(A,(n-1)*k);
else return quick_sum(k,n-1) + quick_mod(A,(n-1)*k);
//這樣的寫法比起上一行的雖然會多一次調用函數的開銷,但可讀性增強,代碼邏輯更清晰
}
int main()
{
//freopen("1588in.txt","r",stdin);
LL k,b,n;
while(~scanf("%I64d%I64d%I64d%I64d",&k,&b,&n,&mod)){
//最後的結果就是 A^b-1 *(A0 + A^k + A^2k +...+ A(n-1)k) 的 a
matrix ans= quick_mod(A,b-1) * quick_sum(k,n);
printf("%I64d\n",ans.a);
}
return 0;
}