【BZOJ】【3240】【NOI2013】矩阵游戏
十进制快速幂+矩阵乘法+常数优化
听说这题还可以强行算出来递推式……然后乘乘除除算出来……
然而蒟蒻选择了一个比较暴力的做法= =
我们发现这个递推的过程是线性的,所以可以用矩阵乘法来表示,$x=a*x+b$这样一个递推式我们可以这样表示:$$\begin{bmatrix} x& 1 \end{bmatrix} * \begin{bmatrix} a& 0 \\ b& 1 \end{bmatrix} $$
那么我们可以令$s_1$表示×a+b,$s_2$表示×c+d,那么我们有$$ans=v * ( ({s_1}^{n-1}*s_2)^{m-1} * {s_1}^{n-1} )$$
然而直接算我给TLE了……
下面说一下常数优化:
我们注意到矩阵乘法的时候有:$$\begin{bmatrix} a& 0 \\ b& 1 \end{bmatrix} * \begin{bmatrix} c& 0 \\ d& 1 \end{bmatrix} = \begin{bmatrix} a*c& 0 \\ a*d+b& 1 \end{bmatrix}$$
也就是说:第二列的0和1是一直不动的……那么我们可以将大部分$O(n^3)$的矩阵乘法过程优化到$O(n^2)$。
这里我们${s_1}^{n-1}$出现了两次,那么我们可以用一个中间变量先存下来,可以减少一次运算(毕竟整个算法的主要部分就是在算这几个power)
1 /************************************************************** 2 Problem: 3240 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:7980 ms 7 Memory:3232 kb 8 ****************************************************************/ 9 10 //BZOJ 3240 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 inline int getint(){ 23 int v=0,sign=1; char ch=getchar(); 24 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 25 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 26 return v*sign; 27 } 28 const int N=1e6+10,INF=~0u>>2,P=1e9+7; 29 typedef long long LL; 30 /******************tamplate*********************/ 31 32 struct Matrix{ 33 int v[2][2]; 34 Matrix(int x=0){F(i,0,1)F(j,0,1)if(i==j)v[i][j]=x;else v[i][j]=0;} 35 int* operator [] (int x){return v[x];} 36 }s1,s2,v; 37 inline Matrix operator * (Matrix a,Matrix b){ 38 Matrix c; 39 if (a[0][1]==0 && a[1][1]==1 && b[0][1]==0 && b[1][1]==1){ 40 c[0][0]=(LL)a[0][0]*b[0][0]%P; 41 c[0][1]=0; 42 c[1][0]=((LL)a[0][0]*b[1][0]+(LL)a[1][0])%P; 43 c[1][1]=1; 44 return c; 45 } 46 F(k,0,1) F(i,0,1) F(j,0,1) 47 c[i][j]=((LL)c[i][j]+(LL)a[i][k]*b[k][j]%P)%P; 48 return c; 49 } 50 inline Matrix Pow(Matrix a,int b){ 51 Matrix c(1); 52 F(i,1,b) c=c*a; 53 return c; 54 } 55 inline Matrix Power(Matrix a,char* s){ 56 Matrix r(1); int l=strlen(s); 57 D(i,l-1,0){ 58 if (s[i]-'0') r=r*Pow(a,s[i]-'0'); 59 a=Pow(a,10); 60 } 61 return r; 62 } 63 char n[N],m[N]; 64 int main(){ 65 #ifndef ONLINE_JUDGE 66 freopen("3240.in","r",stdin); 67 freopen("3240.out","w",stdout); 68 #endif 69 scanf("%s",n); scanf("%s",m); 70 int l1=strlen(n)-1; 71 while(n[l1]=='0') n[l1--]='9'; 72 n[l1]--; 73 l1=strlen(m)-1; 74 while(m[l1]=='0') m[l1--]='9'; 75 m[l1]--; 76 // printf("%s %s\n",n,m); 77 int a,b,c,d; 78 a=getint(); b=getint(); c=getint(); d=getint(); 79 v[0][0]=v[0][1]=1; v[1][0]=v[1][1]=0; 80 s1[0][0]=a; s1[0][1]=0; s1[1][0]=b; s1[1][1]=1; 81 s2[0][0]=c; s2[0][1]=0; s2[1][0]=d; s2[1][1]=1; 82 Matrix s3=Power(s1,m); 83 v=v*(Power(s3*s2,n)*s3); 84 printf("%d\n",v[0][0]); 85 return 0; 86 }
3240: [Noi2013]矩阵游戏
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 890 Solved: 390
[Submit][Status][Discuss]
Description
婷婷是个喜欢矩阵的小朋友,有一天她想用电脑生成一个巨大的n行m列的矩阵(你不用担心她如何存储)。她生成的这个矩阵满足一个神奇的性质:若用F[i][j]来表示矩阵中第i行第j列的元素,则F[i][j]满足下面的递推式:
F[1][1]=1
F[i,j]=a*F[i][j-1]+b (j!=1)
F[i,1]=c*F[i-1][m]+d (i!=1)
递推式中a,b,c,d都是给定的常数。
现在婷婷想知道F[n][m]的值是多少,请你帮助她。由于最终结果可能很大,你只需要输出F[n][m]除以1,000,000,007的余数。
Input
一行有六个整数n,m,a,b,c,d。意义如题所述
Output
包含一个整数,表示F[n][m]除以1,000,000,007的余数
Sample Input
Sample Output
HINT
样例中的矩阵为:
1 4 7 10
26 29 32 35
76 79 82 85
1<=N,M<=10^1000 000,a<=a,b,c,d<=10^9