2019牛客多校B generator 1——十进制快速幂

题目

已知 $x_i = ax_i + bx_{i-1}$,求 $x_n \% MOD$.($1\leq n\leq 10^{(10^6)}$)

分析

写成矩阵快速幂的形式,相当于求转移矩阵的 $n$ 次幂。

由于 $n$ 过大,只能用字符串形式保存,如果转成二进制复杂度过高,就直接用十进制好了。

其实十进制快速幂和二进制几乎一样,都是倍增的思想。

ll qpow(ll a, ll b, ll p)
{
    ll ret = 1;
    while(b)
    {
        if(b&1) ret = ret*a%p;
        a = a*a%p;
        b >>= 1;
    }
    return ret;
}

inline ll shi_pow(ll a, ll b, ll p)
{
    ll ret = 1;
    while(b)
    {
        ll yu = b%10;
        if(yu) ret = ret*qpow(a,yu,p)%p;
        a = qpow(a, 10, p);
        b /= 10;
    }
    return ret;
}

二进制更快,里面能用二进制的换成了二进制。

回到题目,将字符串 $n$ 从高到低就是十进制,与上面类似

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1000000+10;
ll x0,x1,a,b,mod;
char s[N];

struct Mat{
    int r,c;
    ll m[3][3];
    Mat(){
        //memset(m,0,sizeof(m));
        for(int i = 0;i < 3;i++)
            for(int j = 0;j < 3;j++)
                m[i][j]=0;
    }
};

inline Mat mmul(Mat x,Mat y,ll p){
    Mat ans;
    ans.r=x.r;
    ans.c=y.c;
    for(int i=0;i<x.r;i++)
        for(int k=0;k<x.c;k++)
            for(int j=0;j<y.c;j++){
                ans.m[i][j] = (ans.m[i][j] + x.m[i][k]*y.m[k][j])%p;
            }
    return ans;
}

inline Mat mpow(Mat x,ll y,ll p){
    Mat ans;
    ans.r=x.r;
    ans.c=x.c;
    for(int i=0;i<ans.c;i++) ans.m[i][i]=1;
    while(y){
        if(y&1) ans=mmul(ans,x,p);
        x=mmul(x,x,p);
        y>>=1;
    }
    return ans;
}

inline Mat m_shi_pow(Mat x,char* s,ll p){
    Mat ans;
    ans.r=x.r;
    ans.c=x.c;
    int len = strlen(s);
    for(int i=0;i<ans.c;i++) ans.m[i][i]=1;
    while(len--){
        int yu = (s[len]-'0');  //printf("yu:%d\n", yu);
        if(yu) ans=mmul(ans,mpow(x, yu, p),p);
        x=mpow(x,10,p);
    }
    return ans;
}

int main(){
    scanf("%lld%lld%lld%lld", &x0, &x1, &a, &b);
    scanf("%s%lld", s, &mod);
    Mat A,T;
    A.r=2; A.c=1;
    A.m[0][0]=x1; A.m[1][0]=x0;
    T.r=2; T.c=2;
    T.m[0][0]=a; T.m[0][1]=b; T.m[1][0]=1; T.m[1][1]=0;
    T = m_shi_pow(T, s, mod);
    A = mmul(T, A, mod);
    printf("%lld\n", A.m[1][0]);

    return 0;
}

这题有点卡常,加些常数优化才抖过去。

猜你喜欢

转载自www.cnblogs.com/lfri/p/11297999.html
今日推荐