1. 程式人生 > >luogu3263/bzoj4002 有意義的字串 (數學+矩陣快速冪)

luogu3263/bzoj4002 有意義的字串 (數學+矩陣快速冪)

首先我們發現$\frac{b+\sqrt{d}}{2}$這個形式好像一元二次方程的求根公式啊(???反正我發現不了)

然後我們又想到雖然這個東西不好求但是$(\frac{b-\sqrt{d}}{2})^n$好像挺好求的啊(???反正我想不到)(由題目給的範圍,這玩意在(-1,1))

於是把這個方程寫出來:$x^2-b+\frac{b^2-d}{4}=0$,設它的兩根是$x_1=\frac{b+\sqrt{d}}{2} , x_2=\frac{b-\sqrt{d}}{2}$

於是就是要求$\lfloor x_1^n+x_2^n-x_2^n \rfloor$

我們把$x_1^n+x_2^n$單拎出來,分解一下,得到$x_1^n+x_2^n = (x_1+x_2)(x_1^{n-1}+x_2^{n-1}) - x_1x_2(x_1^{n-2}+x_2^{n-2}) $

然後$x_1+x_2$和$x_1x_2$可以用韋達定理算,再把$x_1^i+x_2^i$設成f[i],就可以用矩陣快速冪優化了

可以發現它是個整數,最後討論一下$x_2^n$就行了

(模數巨大,不光要用龜速乘,還要用unsigned long long)

 1 #include<bits/stdc++.h>
 2 #define CLR(a,x) memset(a,x,sizeof(a))
 3 using namespace std;
 4 typedef long long ll;
 5 typedef unsigned long long ull;
 6
typedef pair<int,int> pa; 7 const ull P=7528443412579576937; 8 9 inline ll rd(){ 10 ll x=0;char c=getchar();int neg=1; 11 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 12 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 13 return x*neg; 14 } 15 16
ull fmul(ull a,ull b){ 17 ull re=0; 18 while(b){ 19 if(b&1) re=(re+a)%P; 20 a=(a+a)%P,b>>=1; 21 }return re; 22 } 23 24 ull b,d,n; 25 26 ull fpow(ull x){ 27 ull f[2];f[0]=b,f[1]=2; 28 ull m[2][2],tmp[2][2]; 29 m[0][0]=b,m[0][1]=1,m[1][0]=(d-b*b)/4,m[1][1]=0; 30 while(x){ 31 if(x&1){ 32 CLR(tmp,0); 33 for(int i=0;i<=1;i++){ 34 for(int j=0;j<=1;j++){ 35 tmp[0][i]=(tmp[0][i]+fmul(f[j],m[j][i]))%P; 36 } 37 } 38 f[0]=tmp[0][0],f[1]=tmp[0][1]; 39 } 40 CLR(tmp,0); 41 for(int i=0;i<=1;i++){ 42 for(int j=0;j<=1;j++){ 43 for(int k=0;k<=1;k++){ 44 tmp[i][j]=(tmp[i][j]+fmul(m[i][k],m[k][j]))%P; 45 } 46 } 47 } 48 memcpy(m,tmp,sizeof(m)); 49 x>>=1; 50 }return f[0]; 51 } 52 53 int main(){ 54 //freopen("","r",stdin); 55 int i,j,k; 56 b=rd(),d=rd(),n=rd(); 57 if(n==0) printf("1\n"); 58 else{ 59 ull ans=fpow(n-1); 60 if(!(n&1)&&d!=b*b) ans=(P+ans-1)%P; 61 printf("%lld\n",ans); 62 } 63 64 return 0; 65 }