HDOJ 2604 Queuing 解题报告
A了这一题,学了不少东西,说说。
首先,题意是计算a[L]%m的值。而a[1]=2,a[2]=4,a[3]=6,a[4]=9。L的范围比较夸张,[0,1000000]。
E-queues串的结尾必然是[1]:000,[2]:100,[3]:001,[4]:011,[5]:010,[6]:110
在长度为n-1的E-queues串加0或1,则
a[n][1]=a[n-1][1]+a[n-1][2]
a[n][2]=a[n-1][5]+a[n-1][6]
a[n][3]=a[n-1][1]+a[n-1][2]
a[n][4]=a[n-1][3]
a[n][5]=a[n-1][3]
a[n][3]=a[n-1][4]
按照这种方式,可以得到:a[n]=a[n-1]+a[n-3]+a[n-4]
得到递推公式,一般来说就可以解决问题了,可是题目给的L范围比较大。。。直接算a[n]几乎不可能,很容易超时。
笔者想到这里就卡住了,就在网上找了一下大牛的方法,发现都是用 二分快速幂 来做的。
矩阵是大一的时候学习的,至今没怎么用过,没想到今天派上了用场。
二分快速幂,说的简单一点,就是充分利用每一次的运算结果。例如计算A的n次方:
res=1; while(n) { if(n&1) res=res*A; n>>=1; A=A*A; }
常规的方法要计算n次,而二分的话,就可以只计算log2 n次。
或者说n=15,res=A^n=(A^8)*(A^4)*(A^2)*(A^1)。每一项都是下一项的平方,这就是2分的思想。
那这道题呢?我们可以构建一个矩阵的式子:
[ a[n], a[n-1], a[n-2], a[n-3] ] = [ a[n-1], a[n-2], a[n-3], a[n-4] ]*[ [1, 0, 1, 1]T, [ 1, 0, 0, 0 ]T, [ 0, 1, 0, 0 ]T, [ 0, 0, 1, 0 ]T ]
额。。。确实很难看,笔者也没办法,意思就是这个意思。T是转置的意思。
根据递推式,可以得到:
[ a[n], a[n-1], a[n-2], a[n-3] ] = [ a[4], a[3], a[2], a[1] ]*[ [1, 0, 1, 1]T, [ 1, 0, 0, 0 ]T, [ 0, 1, 0, 0 ]T, [ 0, 0, 1, 0 ]T ]^(n-4)
对矩阵n-4进行二分快速幂的运算,运算过程中模m,可以很快得到结果。下面是笔者的AC代码:
#include <iostream> using namespace std; const int LEN=4; int m; struct Matrix { int matrix[LEN][LEN]; } original,result; Matrix mul(Matrix &a, Matrix &b) { int i,j,k; Matrix c; memset(c.matrix,0,LEN*LEN*4); for(i=0;i<LEN;i++) for(j=0;j<LEN;j++) for(k=0;k<LEN;k++) { c.matrix[i][j]+=a.matrix[i][k]*b.matrix[k][j]; c.matrix[i][j]%=m; } return c; } int main() { int i,n,sum; int s[5]={9,6,4,2,0}; while(cin>>n>>m) { if(n<5) cout<<s[4-n]%m<<endl; else { sum=0; n-=4; memset(original.matrix,0,LEN*LEN*4); memset(result.matrix,0,LEN*LEN*4); for(i=0;i<LEN;i++) original.matrix[i][(i+1)%4]=result.matrix[i][i]=1; original.matrix[0][0]=original.matrix[2][0]=1; while(n) { if(n&1) result=mul(result,original); n>>=1; original=mul(original,original); } for(i=0;i<LEN;i++) { sum+=result.matrix[i][0]*s[i]; sum%=m; } cout<<sum<<endl; } } }
哈哈,IT小男孩原创~http://www.cnblogs.com/IT-BOY