1. 程式人生 > >【bzoj3240 && 洛谷P1397】矩陣遊戲[NOI2013](矩陣乘法+卡常)

【bzoj3240 && 洛谷P1397】矩陣遊戲[NOI2013](矩陣乘法+卡常)

queue tle ext 矩陣乘法 gin click -- 常數 dot

  題目傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=3240

  這道題其實有普通快速冪+費馬小定理的解法……然而我太弱了,一開始只想到了矩陣乘法的方法。

  首先定義兩個矩陣:

  $ A_{1} = \begin{bmatrix} a & b \\ 0 & 1 \end{bmatrix} $
  $ A_{2} = \begin{bmatrix} c & d \\ 0 & 1 \end{bmatrix} $

  於是我們就可以得到這樣的式子:

  $ \begin{aligned} \begin{bmatrix} f_{n,m} \\ 1 \end{bmatrix} & = A_{1} \begin{bmatrix} f_{n,m-1} \\ 1 \end{bmatrix} \\ & = A_{1}^{m-1} \begin{bmatrix} f_{n,1} \\ 1 \end{bmatrix} \\ & = A_{1}^{m-1} A_{2}\begin{bmatrix} f_{n-1,m} \\ 1 \end{bmatrix} \\ & = ( A_{1}^{m-1} A_{2} )^{n-1} \begin{bmatrix} f_{1,m} \\ 1 \end{bmatrix} \\ & = ( A_{1}^{m-1} A_{2} )^{n-1} A_{1}^{m-1} \begin{bmatrix} f_{1,1} \\ 1 \end{bmatrix} \end{aligned} $

  然後用一發10進制矩陣快速冪能解決這道題了。

  然而……這種做法跑的極慢。在洛谷上還能以近2000msAC,放到bzoj的6元cpu上跑就有些力不從心了,,所以得卡常數。

  經過了十幾次提交,使用了奧義·卡常數:10^18進制進制快速冪+循環展開+register後終於卡進了時限。。。

  代碼:

技術分享圖片
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include
<algorithm> #include<queue> #include<vector> #define ll long long #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define inf 0x3f3f3f3f #define mod 1000000007 #define base 1000000000000000000ll #define eps 1e-18 inline ll read() { ll tmp=0; char c=getchar(),f=1; for(;c<0||
9<c;c=getchar())if(c==-)f=-1; for(;0<=c&&c<=9;c=getchar())tmp=tmp*10+c-0; return tmp*f; } using namespace std; struct mat{ ll num[2][2]; }mat1,mat2; struct hp{ ll num[1000010]; int len; }n,m; char s[1000010],t[1000010]; ll a,b,c,d; mat times(mat a,mat b) { mat c; c.num[0][0]=(a.num[0][0]*b.num[0][0]+a.num[0][1]*b.num[1][0])%mod; c.num[0][1]=(a.num[0][0]*b.num[0][1]+a.num[0][1]*b.num[1][1])%mod; c.num[1][0]=(a.num[1][0]*b.num[0][0]+a.num[1][1]*b.num[1][0])%mod; c.num[1][1]=(a.num[1][0]*b.num[0][1]+a.num[1][1]*b.num[1][1])%mod; return c; } mat power_num(mat a,ll b) { mat ans; ans.num[0][0]=ans.num[1][1]=1; ans.num[0][1]=ans.num[1][0]=0; while(b){ if(b&1)ans=times(ans,a); a=times(a,a); b>>=1; } return ans; } mat power(mat a,hp b) { mat ans; ans.num[0][0]=ans.num[1][1]=1; ans.num[0][1]=ans.num[1][0]=0; for(register int i=1;i<=b.len;i++){ ans=times(ans,power_num(a,b.num[i])); a=power_num(a,base); } return ans; } int main() { register int i; scanf("%s",s); scanf("%s",t); a=read(); b=read(); c=read(); d=read(); mat1.num[0][0]=a; mat1.num[0][1]=b; mat1.num[1][0]=0; mat1.num[1][1]=1; mat2.num[0][0]=c; mat2.num[0][1]=d; mat2.num[1][0]=0; mat2.num[1][1]=1; int len1=strlen(s),len2=strlen(t); for(i=1;i*18<=len1;i++){ n.num[i]=0; for(register short j=18;j;j--) n.num[i]=n.num[i]*10+s[len1-(i-1)*18-j]-0; } n.len=len1/18; if(len1%18){ n.num[++n.len]=0; for(register short j=0;j<len1%18;j++)n.num[n.len]=n.num[n.len]*10+s[j]-0; } for(i=1;i*18<=len2;i++){ m.num[i]=0; for(register short j=18;j;j--) m.num[i]=m.num[i]*10+t[len2-(i-1)*18-j]-0; } m.len=len2/18; if(len1%18){ m.num[++m.len]=0; for(register short j=0;j<len2%18;j++)m.num[m.len]=m.num[m.len]*10+t[j]-0; } n.num[1]-=1; for(i=1;i<=n.len;i++)if(n.num[i]<0)n.num[i]+=base,n.num[i+1]-=1; if(n.num[n.len]==0&&n.len>1)--n.len; m.num[1]-=1; for(i=1;i<=m.len;i++)if(m.num[i]<0)m.num[i]+=base,m.num[i+1]-=1; if(m.num[m.len]==0&&m.len>1)--m.len; mat hang=power(mat1,m); mat ans=times(power(times(hang,mat2),n),hang); printf("%lld\n",(ans.num[0][0]+ans.num[0][1])%mod); }
bzoj3240

  下次補個快速冪+費馬小定理的解法。。。

【bzoj3240 && 洛谷P1397】矩陣遊戲[NOI2013](矩陣乘法+卡常)