【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 }
View Code

3240: [Noi2013]矩阵游戏

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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

3 4 1 3 2 6

Sample Output

85

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

Source

[Submit][Status][Discuss]
posted @ 2015-06-07 16:21  Tunix  阅读(...)  评论(...编辑  收藏