2019牛客暑期多校训练营(五)
声明:本文章题目来源均为牛客网
链接:https://ac.nowcoder.com/acm/contest/885#question
B-generator 1
题目链接:https://ac.nowcoder.com/acm/contest/885/B
题目类型:矩阵乘法,矩阵快速幂,十进制快速幂
题目大意
给四个正整数 X0,X1,a,b,然后有一个公式 Xi=a*Xi-1+b*Xi-2(i>2),题目给出两个正整数n和mod,然后输出Xn%mod的值,n是一个很大的数。
(1 < X0,X1,a,b < 1e9),(1e9 < mod <2e9),(2 < n < 1e1e6)
题目理解
题目给了一个递推式,而且n很大,可以用矩阵来写。化成化成矩阵然后用矩阵快速幂,对于n为1e1e6,用二进制倍增是解决不了问题的,所以要用十进制倍增同时用二进制倍增优化。和二进制快速幂差不多的思想,十进制快速幂犹如将3419拆成31x9 * 310x1 * 3100x4。上个板子应该就好理解了。
typedef long long ll;
ll pow2(ll base,ll k,ll mod){//二进制
ll ans=1;
while(k){
if(k&1) ans=(ans*base)%mod;
k>>=1;
base=(base*base)%mod;
}
return ans%mod;
}
//s为用字符串存放的大数
ll pow10(ll base,string s,ll mod){//十进制
ll ans=1,res=1;
ll len=s.size();
for(int i=len-1;i>=0;i--){
ll k=s[i]-'0';
res=pow2(base,k,mod);
ans=(ans*res)%mod;
base=pow2(base,10,mod);
}
return ans%mod;
}
理解了十进制快速幂,那么这题就和普通的矩阵快速幂差不多了。
AC代码
//十进制快速幂
#include<iostream>
#include<cstdio>
#include<string>
#define ll long long
#define sc(x) scanf("%lld",&x);
using namespace std;
ll x0,x1,a,b,mod;
string n;
struct jz{
ll sq[2][2];
jz(){
sq[0][0]=1ll;sq[0][1]=0ll;sq[1][0]=0ll;sq[1][1]=1ll;
}
};
jz mul(jz j1,jz j2){
jz ans;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
ll x=0;
for(int k=0;k<2;k++)
x+=((ll)j1.sq[i][k]*j2.sq[k][j])%mod;
ans.sq[i][j]=x%mod;
}
return ans;
}
jz pow2(jz t,ll k){
jz tt;
while(k){
if(k&1) tt=mul(tt,t);
k>>=1;
t=mul(t,t);
}
return tt;
}
jz pow10(jz t){
jz t1,t2;
for(int i=n.size()-1;i>=0;i--){
ll k=n[i]-'0';
t1=pow2(t,k);
t2=mul(t2,t1);
t=pow2(t,10);
}
return t2;
}
int main(){
sc(x0);sc(x1);sc(a);sc(b);cin >> n;sc(mod);
x1%=mod;x0%=mod;
for(int i=n.size()-1;i>=0;i--){
if(n[i]=='0')
n[i]='9';
else{
n[i]=char(n[i]-1);break;
}
}
jz t;
t.sq[0][0]=a;t.sq[0][1]=1;t.sq[1][0]=b;t.sq[1][1]=0;
t=pow10(t);
printf("%lld",((t.sq[0][0]*x1)%mod+(t.sq[1][0]*x0)%mod)%mod);
return 0;
}
后记
这道题不难。不知道10进制快速幂的话就另说。