洛谷P3263 [JLOI2015]有意义的字符串(数学+矩阵乘法)

写篇题解纪念下,毕竟我一道题犯这么多SB错误(sizeof(0),long long传参传成int,没有赋值等)也是不容易= =

考虑数列\(a_n=(\frac{b+\sqrt{d}}{2})^n+(\frac{b-\sqrt{d}}{2})^n\),则数列\(a_n\)的特征方程为\(x^2-bx-\frac{d-b^2}{4}=0\)

\(\{a_n\}\)的递推公式为\(a_n=ba_{n-1}+\frac{d-b^2}{4}a_{n-2}\),边界是\(a_0=2\),\(a_1=b\)

因为根据题目条件得\(4|d-b^2\),所以\(a_n\)为整数,所以\((\frac{b+\sqrt{n}}{2})^n+(\frac{b-\sqrt{n}}{2})^n\)为整数

又因为\(b^2 \leq d <(b+1)^2\),所以\(-1<\frac{b-\sqrt{d}}{2}\leq0\),很小的一个数,所以我们可以直接矩阵快速幂求出\(a_n\)再计算出答案

\(n\)为偶数且\(b^2\neq d\)时,\(0\leq(\frac{b-\sqrt{d}}{2})^n<1\),此时\(ans=a_n-1\)

其它情况,\(-1<(\frac{b-\sqrt{d}}{2})^n\leq 0\),此时\(ans=a_n\)

代码:

#include <bits/stdc++.h>
#define ll long long
#define mod 7528443412579576937ll
using namespace std;

ll Mar[2][2],E[2][2],tmp[2][2];

inline ll add(ll x,ll y){ return x+y>=mod||x+y<0?x-mod+y:x+y; }
inline ll Minus(ll x,ll y){ return x-y<0?x-y+mod:x-y; }
inline ll qorg(ll x,ll y){
    ll d=(ll)(x*(long double)y/mod+0.5);
    return Minus(x*y,d*mod);
}
void mul(ll a[][2],ll b[][2]){
    memset(tmp,0,sizeof(tmp));
    for(int i=0;i<2;++i)
        for(int k=0;k<2;++k)
            for(int j=0;j<2;++j)
                tmp[i][j]=add(tmp[i][j],qorg(a[i][k],b[k][j]));
    for(int i=0;i<2;++i)
        for(int j=0;j<2;++j)
            a[i][j]=tmp[i][j];
}
void qpow(ll x){ for(;x;x>>=1,mul(E,E)) if(x&1) mul(Mar,E); }

int main(){ 
    ll b,d,n;
    scanf("%lld%lld%lld",&b,&d,&n);
    Mar[0][0]=b,Mar[0][1]=2;
    E[0][0]=b,E[1][0]=(d-b*b)/4ll,E[0][1]=1;
    if(n==0){ puts("1");return 0; }
    qpow(n-1);
    if(n%2==0 && b*b!=d) Mar[0][0]=Minus(Mar[0][0],1);
    printf("%lld",Mar[0][0]%mod);
}

猜你喜欢

转载自www.cnblogs.com/PsychicBoom/p/11102788.html