矩阵快速幂解法:
这是一个类似斐波那契数列的矩乘快速幂,所以推荐大家先做一下下列题目:(会了,差不多就是多倍经验题了)
注:如果你不会矩阵乘法,可以了解一下P3390的题解
由题意可得:相邻两个珠子中必有金属性珠子。这其实就可以理解为不能有连续的两个木属性珠子。这样一看,此题就和P4838 P哥破解密码差不多了。只不过这题是个2*2矩阵乘法
进入正文:
我们先一次将1~n中每一个珠子的情况枚举
// n=1 n=2 n=3 n=4 n=5 n=6 .......
//可放金属性珠子: 1 2 3 5 8 13 .......
//可放木属性珠子: 1 1 2 3 5 8 .......
不难发现这就是一个斐波那契数列的递推
但是:这是一个手环!
所以第一个珠子与最后一个手环是相连的,他们会互相影响!
不过他们只会影响对方而不会影响其他珠子,我们可以将第一颗珠子选金属性与木属性这两种情况分开:
//第一颗珠子为金属性: 若 n=5
// 1 2 3 4 n .......
//金属性: 1 1 2 3 5 .......
//木属性: 0 1 1 2 3 .......
//第一颗珠子为木属性: n=5
// 1 2 3 4 n .......
//金属性: 0 1 1 2 3 .......
//木属性: 1 0 1 1 0 .......
//最后一颗不能为木!
//两种情况加起来就是样例1的解了
所以此题就是求斐波那契数列第n项 加 第n-1项的两倍
然后就可矩阵快速幂了!递推矩阵如下
// 1 1
// 0 1
不过我们当然不能止步于此:
因为还有一种更无脑有效的方法:
既然矩阵可以快速幂,那么说明每两个递推数(即答案)之间的递推矩阵是一样的!
所以我们可以先手算两组结果,然后直接推出递推矩阵:
// 3 4 乘 递推矩阵 = 4 7
// 解上述方程得递推矩阵为:
// 0 1
// 3 4 乘 = 4 7
// 1 1
下面上代码:
#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007//简化一下
struct ju{
long long a[2][2];//不用long long只有8分哦(亲测QAQ)
ju operator *(const ju &x){
ju res;//这里需要另外建个矩阵存答案
memset(res.a,0,sizeof(res.a));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
res.a[i][j]=(res.a[i][j]+a[i][k]%mod*(x.a[k][j])%mod)%mod;
return res;
} //重载运算符,(可以写成函数)
}base,ans;//两个基本矩阵
int main(){
long long n,t;
scanf("%lld",&t);//只有t就不写快读了
while(t--){
scanf("%lld",&n);n--;//n要减一,不然会错 QAQ
base.a[0][0]=0;base.a[0][1]=1;base.a[1][0]=1;base.a[1][1]=1;
ans.a[0][0]=2;ans.a[0][1]=1;ans.a[1][0]=0;ans.a[1][1]=0;//初始化
while(n){//快速幂,(也可以写成函数)
if(n%2==1) ans=ans*base;
base=base*base;
n/=2;
}
printf("%lld\n",ans.a[0][1]%mod);
} //输出
return 0;
}
代码中ans的初始化已经是n=1时的答案了所以n要减一。
啊,写题解好累啊,是我太蒟了吗。。