【BZOJ4002】[JLOI2015]有意义的字符串 - 矩阵乘法
题意:
给出b,d,n,求$\lfloor(\frac{b+\sqrt{d}}{2})^n\rfloor \mod 999999999999999989$(原题是7528443412579576937)。
$n\leq 10^{18}$
$0<b^2\leq d<(b+1)^2\leq 10^{18}$
$b \mod 2=1$
$d \mod 4=1$
对于20%的数据有$b=1,d=5$
题解:
我是不知道这题跟字符串有什么关系。。。
场上有40%的数据是$n\leq 5$然而我们都没搞出来。。。
本质是发现性质然后乱搞。。。(这场数学竞赛的本质)
观察式子$(\frac{b+\sqrt{d}}{2})^n$,发现他是一个很像共轭根式的东西,那可以把另一半搞出来,得到
$(\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n$;
显然这个东西一定是整数,不妨设个通项$a_n=(\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n$,则答案就是$a_n-(\frac{b-\sqrt{d}}{2})^n$;
然后我们可以通过一些黑科技用通项把递推式还原出来:把两个共轭根式看成特征方程的两个解,再通过韦达定理就可以把原来的系数解出来。。。
有兴趣的同学可以自己算一下,这里算出来特征方程是$x^2-bx+\frac{b^2-d}{4}=0$,那么还原出来递推式就是
$a_n=b\times a_{n-1}+\frac{d-b^2}{4}\times a_{n-2}$,其中$a_0=2,a_1=b$
所以可以用矩阵快速乘来搞定数列,再考虑后面的$(\frac{b-\sqrt{d}}{2})^n$;
由于数据有$b^2\leq d<(b+1)^2$或$b=1,d=5$,所以$(\frac{b-\sqrt{d}}{2})^n∈(-1,0]$,当且仅当$d≠b^2$且$n$为偶数时要把答案减一。
时间复杂度$O(log^2n)$,但是原题模数爆longlong,所以要手写快速乘。。。
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define eps 1e-4
7 #define mod 999999999999999989ll
8 using namespace std;
9 typedef unsigned long long ull;
10 struct sq{
11 ull a[2][2];
12 sq(){
13 a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;
14 }
15 void init(){
16 a[0][0]=a[1][1]=1;
17 }
18 }a,ans;
19 ull calc(ull x,ull y){
20 ull ret=0;
21 for(;y;y>>=1,x=(x+x>mod)?x+x-mod:x+x){
22 if(y&1)ret=(ret+x>mod)?ret+x-mod:ret+x;
23 }
24 return ret;
25 }
26 sq operator *(const sq a,const sq b){
27 sq ret;
28 for(int i=0;i<2;i++)for(int j=0;j<2;j++)for(int k=0;k<2;k++){
29 ret.a[i][j]=(ret.a[i][j]+calc(a.a[i][k],b.a[k][j]))%mod;
30 }
31 return ret;
32 }
33 sq pw(sq x,ull y){
34 sq ret;
35 ret.init();
36 for(;y;y>>=1,x=x*x){
37 if(y&1)ret=ret*x;
38 }
39 return ret;
40 }
41 ull b,d,n;
42 int main(){
43 scanf("%llu %llu %llu",&b,&d,&n);
44 a.a[1][0]=1;
45 a.a[0][1]=(d-b*b)/4;
46 a.a[1][1]=b;
47 ans.a[0][0]=2;
48 ans.a[0][1]=b;
49 ans=ans*pw(a,n);
50 if(n%2==0&&d!=b*b)ans.a[0][0]--;
51 printf("%llu",ans.a[0][0]);
52 return 0;
53 }