寺庙逃脱

题目大意

有q个询问,每个询问给出
给出a[0]=k,和递推式

a[n]=Aa[n1]+BCa[n1]+D

求a[n] mod p的值。
数据范围q<=104,n<=1018,p<=109,|A,B,C,D|<p,p

矩阵乘法

a[n+1]=Aa[n]+BCa[n]+D

考虑往下代入递推式

a[n+1]=AAa[n1]+BCa[n1]+D+BCAa[n1]+BCa[n1]+D+D

分子分母同时乘上C*a[n-1]+D,于是

a[n+1]=A(Aa[n1]+B)+B(Ca[n1]+D)C(Aa[n1]+B)+D(Ca[n1]+D)

提出a[n-1]

a[n+1]=(A2+BC)a[n1]+AB+BD(AC+DC)a[n1]+BC+D2

容易发现A,B,C,D的转移矩阵是固定的。
然后就可以用矩阵乘法加快速幂。
逆元就随意搞搞就行啦。

代码

我脑残打成了4*4的转移矩阵,其实2*2的就行了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
ll n,k,a,b,c,d,p;
int read(ll &n){
    char ch=getchar();
    while((ch!='-')&&((ch<'0')||(ch>'9')))ch=getchar();
    ll q=0,w=1;if(ch=='-')w=-1,ch=getchar();
    while(ch>='0' && ch<='9')q=q*10+ch-48,ch=getchar();n=q*w;
}
struct ju{
    ll w[5][5];
} f,g,one;
ju operator *(ju a,ju b){
    ju c;
    memset(c.w,0,sizeof(c.w));
    for (int i=1;i<=4;i++)
    for (int k=1;k<=4;k++)
    for (int j=1;j<=4;j++) {
    c.w[i][k]=(c.w[i][k]+a.w[i][j]*b.w[j][k]%p)%p;
    }
    return c;
}
ll qss(ll x,ll y){
    if (y==0) return 1;
    if (y==1) return x;
    ll s=qss(x,y/2);s=s*s%p;
    if (y%2==0) return s;
    s=s*x%p;return s;
}
ju qs(ju g,ll y){
    if (y==0) return one;
    if (y==1) return g;
    ju c=qs(g,y/2);c=c*c;
    if (y%2==0) return c;
    c=c*g;return c;
}
int main(){
    one.w[1][1]=one.w[2][2]=one.w[3][3]=one.w[4][4]=1;
    ll q;read(q);
    for (q=q;q>0;q--){
        read(k);read(a);read(b);read(c);read(d);read(n);read(p);
        if (n==0){printf("%lld",k);continue;}
        a=(a+p)%p;b=(b+p)%p;c=(c+p)%p;d=(d+p)%p;
        memset(g.w,0,sizeof(g.w));memset(f.w,0,sizeof(f.w));
        f.w[1][1]=a;f.w[1][2]=b;f.w[1][3]=c;f.w[1][4]=d;
        g.w[1][1]=a;g.w[1][2]=b;g.w[1][3]=g.w[2][1]=g.w[2][4]=c;
        g.w[2][2]=g.w[3][3]=g.w[4][4]=d;
        g=qs(g,n-1);
        f=f*g;
        ll s1=(f.w[1][1]*k+f.w[1][2])%p,s2=(f.w[1][3]*k+f.w[1][4])%p;
        s2=qss(s2,p-2);
        printf("%lld\n",s1*s2%p);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值