“玲珑杯”ACM比赛 Round #12 (D) 矩阵快速幂的时间优化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“玲珑杯”ACM比赛 Round #12 (D) 矩阵快速幂的时间优化相关的知识,希望对你有一定的参考价值。
//首先,感谢Q巨
定义状态向量b[6]
b[0]:三面临红色的蓝色三角形个数
b[1]:两面临红色且一面临空的蓝色三角形个数
b[2]:一面临红色且两面临空的蓝色三角形个数
b[3]:三面临红色的黄色三角形个数
b[4]:两面临红色且一面临绿+的黄色三角形个数
b[5]:一面临红色且两面临绿+的黄色三角形个数
转移矩阵:
[3 1 0 0 0 0;
0 2 2 0 0 0;
0 1 3 0 0 0;
3 2 1 0 0 0;
0 0 0 6 3 0;
0 0 0 0 2 4]
最朴素的TLE代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=6; const LL mod=1234321237; LL b[N]= {0,0,6,0,0,0}; //此处初始化列向量 LL hh[N][N]={{3,1,0,0,0,0}, {0,2,2,0,0,0}, {0,1,3,0,0,0}, {3,2,1,0,0,0}, {0,0,0,6,3,0}, {0,0,0,0,2,4} }; struct Mat { LL mat[N][N]; } A; Mat Mut(Mat a,Mat b) { Mat c; memset(c.mat,0,sizeof(c.mat)); for(int k=0; k<N; k++) for(int i=0; i<N; i++) if(a.mat[i][k]) for(int j=0; j<N; j++) { c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%mod; c.mat[i][j]=c.mat[i][j]%mod; } return c; } Mat Qpow(Mat a,LL n) { Mat c; for(int i=0; i<N; ++i) for(int j=0; j<N; ++j) c.mat[i][j]=(i==j); for(; n; n>>=1) { if(n&1) c=Mut(c,a); a=Mut(a,a); } return c; } void cal(Mat A,LL n,LL b[],LL& Fn,LL& Gn) { Mat A_=Qpow(A,n-1); Fn=Gn=0; LL c[N]={0}; for(int i=0;i<N;i++) for(int j=0;j<N;j++) c[i]=(c[i]+A_.mat[i][j]*b[j])%mod; Fn=(c[0]+c[1]+c[2])%mod; Gn=(c[3]+c[4]+c[5])%mod; } void init_A() { for(int i=0;i<N;i++) for(int j=0;j<N;j++) A.mat[i][j]=hh[i][j]; } int main() { LL n,Fn,Gn; init_A(); while(cin>>n) { if(n==1) { puts("1 0"); continue; } n--; cal(A,n,b,Fn,Gn); //假定下标从第1项开始计数,求第n项 cout<<Fn<<‘ ‘<<Gn<<endl; } }
貌似(只是貌似)被优化但仍然TLE的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=6; const LL mod=1234321237; LL b[N]= {0,0,6,0,0,0}; //此处初始化列向量 LL hh[N][N]={{3,1,0,0,0,0}, {0,2,2,0,0,0}, {0,1,3,0,0,0}, {3,2,1,0,0,0}, {0,0,0,6,3,0}, {0,0,0,0,2,4} }; struct Mat { LL mat[N][N]; } A,F[62]; void printM(Mat x) { puts("================================================================="); for(int i=0;i<N;i++) { for(int j=0;j<N;j++) printf("%10lld",x.mat[i][j]); puts(""); } } Mat Mut(Mat a,Mat b) { Mat c; memset(c.mat,0,sizeof(c.mat)); for(int k=0; k<N; k++) for(int i=0; i<N; i++) if(a.mat[i][k]) for(int j=0; j<N; j++) { c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%mod; c.mat[i][j]=c.mat[i][j]%mod; } return c; } Mat Qpow(Mat a,LL n) { Mat c; for(int i=0; i<N; ++i) for(int j=0; j<N; ++j) c.mat[i][j]=(i==j); for(; n; n>>=1) { if(n&1) c=Mut(c,a); a=Mut(a,a); } return c; } void cal(Mat A,LL n,LL b[],LL& Fn,LL& Gn) { Mat A_; for(int i=0; i<N; ++i) for(int j=0; j<N; ++j) A_.mat[i][j]=(i==j); for(int i=0;i<60&&n;i++,n>>=1) if(n&1) A_=Mut(A_,F[i]); //printM(A_); Fn=Gn=0; LL c[N]={0}; for(int i=0;i<N;i++) for(int j=0;j<N;j++) c[i]=(c[i]+A_.mat[i][j]*b[j])%mod; Fn=(c[0]+c[1]+c[2])%mod; Gn=(c[3]+c[4]+c[5])%mod; } void init_A() { for(int i=0;i<N;i++) for(int j=0;j<N;j++) A.mat[i][j]=hh[i][j]; F[0]=A; for(int i=1;i<61;i++) F[i]=Mut(F[i-1],F[i-1]); } int main() { LL n,Fn,Gn; init_A(); int T; // cin>>T; scanf("%lld",&T); while(T--) { // cin>>n; scanf("%lld",&n); if(n==1) { puts("1 0"); continue; } n-=2; cal(A,n,b,Fn,Gn); //假定下标从第1项开始计数,求第n项 // cout<<Fn<<‘ ‘<<Gn<<endl; printf("%lld %lld\n",Fn,Gn); } }
矩阵相乘一次的复杂度是O(N^3)的,不过预处理2^i(i:0~60)的矩阵后,可以用向量记录中间结果,而矩阵*向量的复杂度为O(N^2)
最终复杂度: O(T * lb(n) * N^2)->O(1e5 * 60 * 36)->O(2e8)
最终可以AC的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=6; const LL mod=1234321237; LL b[N]= {0,0,6,0,0,0}; //此处初始化列向量 LL hh[N][N]={{3,1,0,0,0,0}, {0,2,2,0,0,0}, {0,1,3,0,0,0}, {3,2,1,0,0,0}, {0,0,0,6,3,0}, {0,0,0,0,2,4} }; struct Mat { LL mat[N][N]; } A,F[62]; void printM(Mat x) { puts("================================================================="); for(int i=0;i<N;i++) { for(int j=0;j<N;j++) printf("%10lld",x.mat[i][j]); puts(""); } } Mat Mut(Mat a,Mat b) { Mat c; memset(c.mat,0,sizeof(c.mat)); for(int k=0; k<N; k++) for(int i=0; i<N; i++) if(a.mat[i][k]) for(int j=0; j<N; j++) { c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%mod; c.mat[i][j]=c.mat[i][j]%mod; } return c; } Mat Qpow(Mat a,LL n) { Mat c; for(int i=0; i<N; ++i) for(int j=0; j<N; ++j) c.mat[i][j]=(i==j); for(; n; n>>=1) { if(n&1) c=Mut(c,a); a=Mut(a,a); } return c; } void cal(Mat A,LL n,LL b[],LL& Fn,LL& Gn) { Mat A_; LL c[N]={0,0,6,0,0,0}; for(int i=0; i<N; ++i) for(int j=0; j<N; ++j) A_.mat[i][j]=(i==j); for(int i=0;i<60&&n;i++,n>>=1) if(n&1) { LL tres[6]={0,0,0,0,0,0}; for(int j=0;j<6;j++) //矩阵的行 for(int k=0;k<6;k++) //矩阵的列 tres[j]=(tres[j]+F[i].mat[j][k]*c[k])%mod; for(int j=0;j<6;j++) c[j]=tres[j]%mod; } Fn=Gn=0; Fn=(c[0]+c[1]+c[2])%mod; Gn=(c[3]+c[4]+c[5])%mod; } void init_A() { for(int i=0;i<N;i++) for(int j=0;j<N;j++) A.mat[i][j]=hh[i][j]; F[0]=A; for(int i=1;i<61;i++) F[i]=Mut(F[i-1],F[i-1]); } int main() { LL n,Fn,Gn; init_A(); int T; // cin>>T; scanf("%lld",&T); while(T--) { // cin>>n; scanf("%lld",&n); if(n==1) { puts("1 0"); continue; } n-=2; cal(A,n,b,Fn,Gn); //假定下标从第1项开始计数,求第n项 // cout<<Fn<<‘ ‘<<Gn<<endl; printf("%lld %lld\n",Fn,Gn); } }
以上是关于“玲珑杯”ACM比赛 Round #12 (D) 矩阵快速幂的时间优化的主要内容,如果未能解决你的问题,请参考以下文章