【矩陣乘】【DP】【codevs 1305】Freda的道路
1305 Freda的道路
時間限制: 1 s
空間限制: 128000 KB
題目等級: 大師 Master
題目描述 Description
Freda要到Rainbow的城堡去玩了。我們可以認為兩座城堡位於同一條數軸上,Freda的城堡座標是0,Rainbow的城堡座標是N。正常情況下,Freda會朝著同一個方向(即Rainbow的城堡相對於Freda的城堡的方向)走若干步之後來到Rainbow的城堡,而且步長都為1或2。可是,今天Freda在途中遇見了來自上海的小貓Resodo,驚奇之下,居然有一步走反了方向!不過,Freda並沒有神智不清,它只有一步走反了方向, 而且這一步的步長也是1或2. 同時,Freda並不會路過Rainbow的城堡而不停下來。當然,Freda是在途中遇到Resodo的,所以它不會在 自己家門口就走錯方向。
舉個例子,如果Rainbow的城堡座標是3,那麼下面兩條路徑是合法的:
0->1->2->1->3
0->1->-1->1->3
當然,還有其它的合法路徑。下面這些路徑則是不合法的:
0->-1->1->3 (Freda不可能第一步就走錯方向) 0->1->3(Freda一定是有一步走錯方向的) 0->2->1->0->2->3(Freda只有一步是走錯方向的) 0->-1->0->3(Freda每步的長度一定是1或2) 0->1->2->4->3(Freda不會越過Rainbow的城堡再回來) 0 -> 1 -> 2 -> 3 -> 2 -> 3(Freda一旦到達了Rainbow的城堡,就會停下來)
你現在需要幫助Freda求出,它一共有多少種方法能夠到達Rainbow的城堡呢?
輸入描述 Input Description
一行一個整數N,表示Rainbow城堡的座標
輸出描述 Output Description
一行一個整數,表示Freda到Rainbow城堡的不同路徑數。
由於這個數字可能很大,你只需要輸出它mod 1000000007的結果。
樣例輸入 Sample Input
2
樣例輸出 Sample Output
5
資料範圍及提示 Data Size & Hint
對於第一組樣例,如下5條路徑是合法的:
0->1->0->2 0->1->-1->0->1->2 0->1->-1->0->2 0->1->0->1->2 0->1->-1->1->2
資料範圍與約定
對於10%的資料,N<=20.
對於70%的資料,N<=1000.
對於90%的資料,N<=1000000.
對於100%的資料,N<=10^15.
題解:
因為是討論路徑個數,所以首先想到的是dp。
設f[i]表示在不回頭的情況下到達的路徑數,g[i]表示在一次回頭的情況下到達的路徑數。
由題意可知:
f[i]=f[i-2]+f[i-1](當前只能從兩步前或是一步前轉移來),即Fibonacci數列。
接下來考慮g[i],發現g[i]由四種情況轉移過來,即:
1>g[i-2](在轉過的情況下,由兩步前走來)
2>g[i-1](在轉過的情況下,由一步前走來)
3>f[i+1](一步以後轉回來)
4>f[i+2](兩步以後轉回來)
於是可得轉移方程:
g[i]=g[i-2]+g[i-1]+f[i+1]+f[i+2]
裸的dp可得90 。。。最後一個點是極限資料,DP做不到。於是想到了矩陣乘。
對於以下兩個矩陣:
g[n]可以這樣求得:
於是我們可以這樣求解g[n]:
Code:
DP:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define p 1000000007LL
using namespace std;
long long n,f[1000010],g[1000010];
int main(){
scanf("%d",&n);
if (n==1){
printf("0\n");
return 0;
}
f[1]=f[2]=g[0]=g[1]=1;
for (int i=3; i<=n+1; i++)
f[i]=(f[i-1]+f[i-2])%p;
for (int i=2; i<=n+1; i++)
g[i]=(g[i-2]+g[i-1]+f[i+1]+f[i+2])%p;
printf("%d\n",g[n+1]);
return 0;
}
矩陣乘:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define p 1000000007LL
using namespace std;
long long A[4][4]={{0,1,0,0},{1,1,1,1},{0,0,0,1},{0,0,1,1}};
long long B[4][4],C[4][4];
int main(){
long long n; bool f=true;
scanf("%lld",&n);
if (n<=2){
if (n==1) printf("0\n");
else printf("5\n");
return 0;
}
n-=2;
while (n){
if (n&1){
if (f){
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
B[i][j]=A[i][j];
f=false;
}
else {
memset(C,0,sizeof(C));
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
for (int k=0; k<4; k++)
C[i][j]=(C[i][j]+B[i][k]*A[k][j])%p;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
B[i][j]=C[i][j];
}
}
n>>=1;
memset(C,0,sizeof(C));
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
for (int k=0; k<4; k++)
C[i][j]=(C[i][j]+A[i][k]*A[k][j])%p;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
A[i][j]=C[i][j];
}
long long ans=(B[1][1]*5%p+B[1][2]*5%p+B[1][3]*8%p)%p;
printf("%lld\n",ans);
return 0;
}