【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 }

 

posted @ 2018-08-28 17:01  DCDCBigBig  阅读(146)  评论(0编辑  收藏  举报