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; }
这题有点卡常,加些常数优化才抖过去。
个性签名:时间会解决一切