Problem D: 快速幂-D
Time Limit: 1 Sec Memory Limit: 128 MB
Description
Averyboy最近迷上了中国环游戏,但是他绞尽脑汁也不会把环拆下来,于是他请教聪明的你帮他解决问题。
首先,杆子上有n个环,第一个环可以挂上或拆下。如果你想挂上或拆下第n个环,那必须满足以下条件:
1. 第n-1个环是挂上的
2. 前n-2个环是拆下的
问题是把n个环全部拆下需要的最少操作次数。
Input
输入文件每行一个数n,n <= 1e9,最后一行是数字0。
Output
每行输出一个数X,代表最少操作次数。X可能很大,输出X%201807
解答:
这个题目首先要得到递推式。以五个环为例,先将前3个看为一个整体,取下它,那么第五个环可以取下,再把前三个重新挂上,将剩余四个看成一个整体,一起取下,故f(5)=2f(3)+f(4)+1,由此可得递推式: f(n)=2*f(n-2)+f(n-1)+1。构造式子得:
[f(n),f(n-1),1]=[f(n-1),f(n-2),1][1 1 0
2 0 0
1 0 1]
要算的X即f(n),要算矩阵得n-2次幂,代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
const int N=3;
const int Mod=201807;
typedef long long ll;
using namespace std;
ll f[3],a[3][3];
void multi(ll f[3],ll a[3][3])
{
ll c[3];
memset(c, 0, sizeof(c));
int i,j,k;
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
c[i]+=(ll)f[j]*a[j][i];
c[i]%=Mod;
}
}
memcpy(f,c,sizeof(c));
}
void quick_power(ll a[3][3])
{
ll c[3][3];
memset(c, 0, sizeof(c));
for(int i=0;i<3;i++){
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++)
{
c[i][j]+=(ll)(a[i][k]*a[k][j]);
c[i][j]%=Mod;
}
}}
memcpy(a,c,sizeof(c));
}
int main() {
ll n;
while(~scanf("%lld", &n) && n) {
if(n == 1) {
printf("1\n");
continue;
}
if(n == 2) {
printf("2\n");
continue;
}
f[0] = 2, f[1] = 1, f[2] = 1;
a[0][0] = 1, a[0][1] = 1, a[0][2] = 0;
a[1][0] = 2, a[1][1] = 0, a[1][2] = 0;
a[2][0] = 1, a[2][1] = 0, a[2][2] = 1;
n = n - 2;
for(; n; n >>= 1) {
if(n%2==1) multi(f,a);
quick_power(a);
}
printf("%lld\n", f[0] % Mod);
}
return 0;
}