[题解]bzoj4002(JLOI2015)有意义的字符串

Description

B 君有两个好朋友,他们叫宁宁和冉冉。有一天,冉冉遇到了一个有趣的题目:输入 b;d;n,求
这里写图片描述

Input

一行三个整数 b;d;n

Output

一行一个数表示模 7528443412579576937 之后的结果。

Sample Input

1 5 9

Sample Output

76

HINT

其中 0<b2<=d<(b+1)2<=1018,n<=1018 ,并且 b mod 2=1,d mod 4=1

Solution

看看题目中的式子,想想初中数学,是不是很熟悉啊?
没错,他很像是一元二次方程的根。再联想一下n次方,于是神奇的发现他又像是某数列通项公式中的那个东东。
式子中可以看出,如果一元二次方程 x2=px+q 的一个根为 (b+d2)n ,那么 Δ=p2+4q=d p=b ,所以根据通项公式可以构造出序列:

an=ban1+(db2)4an2

他的通项公式为
an=A(b+d2)n+B(bd2)n

为了方便计算,我们取 A=B=1 ,所以
a0=2
a1=b

而序列a的递推式中 (db2)4 ,由于b mod 2=1,d mod 4=1,所以 (db2)4 一定是整数,所以矩阵乘法直接求出第n项,所以我们要的答案就是
(b+d2)n=an(bd2)n

由于题目中给出 b2<=d<(b+1)2 ,所以 bd2(1,0] ,当且仅当 b2d 且n为偶数时对答案有-1的贡献,所以答案要视情况减一。

注意模数太大,所以要开unsigned long long,并且要用快速乘取模。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

typedef unsigned long long LL;
const LL mod=7528443412579576937;
struct matrix{
    LL a[2][2];
    matrix(){memset(a,0,sizeof a);}
}temp,I,c;
LL b,d,n,ans;

LL ksc(LL x,LL y){
    LL re=0;
    while(y){
        if(y&1)(re+=x)%=mod;
        (x+=x)%=mod;y>>=1;
    }
    return re;
}
matrix cheng(matrix x,matrix y){
    matrix re;
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            for(int k=0;k<2;k++){
                (re.a[i][j]+=ksc(x.a[i][k],y.a[k][j]))%=mod;
            }
        }
    }
    return re;
}
matrix pow(matrix x,LL y){
    matrix re=I;
    while(y){
        if(y&1)re=cheng(re,x);
        x=cheng(x,x);y>>=1;
    }
    return re;
}

int main(){
    cin>>b>>d>>n;
    I.a[0][0]=I.a[1][1]=1;
    temp.a[0][0]=b;temp.a[0][1]=2;
    c.a[0][0]=b;c.a[0][1]=1;c.a[1][0]=(d-b*b)/4;
    if(n==0)return printf("1\n"),0;
    temp=cheng(temp,pow(c,n-1));
    ans=temp.a[0][0];
    if(!(n&1)&&b*b!=d)ans--;
    cout<<(ans+mod)%mod<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值