一道堵了我很久的题,被别人一说,就开窍了
题目描述
有一群一共 n 个妖精和兽排成一队,其中这一队列可以全是妖精或者全是兽,当然也可以是妖精和兽并存。
排队时还规定:兽不能落单(落单了就要被妖精砍死了),再明确一些,就是要么这个队列里面没有兽,如果有兽的话,必须 至少 两个兽肩并肩排在一起。
请问有多少种排法呢?由于数可能很大,请你对这个结果对 1e9 + 7 取模。
输入
多组输入,请处理到文件结束。
数据组数不超过 20000。
一行一个数,代表 (n <= 10^10)。
输出
输出方案数,对 1e9 + 7 取模。
输入样例
1
2
3
输出样例
1
2
4
提示
假设妖精为 G,兽为 B。
n = 1 的情况: G n = 2 的情况: BB GG n = 3 的情况: BBG GGG GBB BBB
注意 n = 1 中没有 B 这种情况,因为这样算落单。
--------------------------------我是分界线-------------------------------------------
从它给的范围((n <= 10^10)),很显然这是一个找规律或者是快速幂的题目。
题目说B(兽,借用题目的提示)不能落单,必须连续的2个或者两个以上才合法,举例的话GBBGBB是合法的。
那么开始找规律:
1. 假设Y(n)表示为,输入为n的输出。
2. 假设T(n)表示为:合法的排列,且最后一个为G。例如:GGGG。
3. 假设S(n)表示为:“缺B合法排列”的排列数。表示在序列的最后,额外添加一个B,能够使这个序列变成合法的,例如:GBBGGGB。
4. 已知:Y(n-1),T(n-1),S(n-1)。
5. 求Y(n):那么我在这Y(n-1)个合法的序列中最后面添加一个G,这个新序列也是合法的,共有Y(n-1)中;我在这Y(n-1)个合法的序列中最后面添加一个B,只有[Y(n-1)-T(n-1)]种新序列是合法的;然后我在“缺B合法排列”后面加一个B,就会有新的S(n-1)中合法排列。Y(n)=Y(n-1)+[Y(n-1)-T(n-1)]+S(n-1)。
6. 求T(n):显然,T(n)=Y(n-1)。在n-1长的合法序列末加G。
7. 求S(n):显然,S(n)=T(n-1)。在n-1长的合法序列且以G结尾后面加个B。
8. 得到Y(n)=2*Y(n-1)-Y(n-2)+Y(n-3)。
得到上面那个式子后我就束手无策了.......在同学的点拨下,想起了矩阵快速幂
其他的没什么说的,一个点被坑了一下,就是要使用long long。初始化的时候不知道为什么,初始化写 0L 也自动转化为int型数据了,最后我尝试初始化为 1000000007000L 就没事了,这也算是一点收获。
最后AC代码:
#include <iostream>
using namespace std;
#define M (long long)(1e9+7)
typedef struct Mat{
long long c[3][3];
}Mat;
Mat calone(Mat a, Mat b);
long long speedcal(Mat s, long long n);
int main()
{
long long N;
while(cin>>N){
Mat sta;//矩阵初始化操作很low...
sta.c[0][0]=4L;sta.c[0][1]=2L;sta.c[0][2]=1L;
sta.c[1][0]=0L;sta.c[1][1]=0L;sta.c[1][2]=0L;
sta.c[2][0]=0L;sta.c[2][1]=0L;sta.c[2][2]=0L;
long long ans=speedcal(sta,N);
cout<<ans<<endl;
}
return 0;
}
long long speedcal(Mat s, long long n)
{
if(n==1)
return s.c[0][2];
else if(n==2)
return s.c[0][1];
else if(n==3)
return s.c[0][0];
else{
Mat m,t=s;
m.c[0][0]=2L;m.c[0][1]=1L;m.c[0][2]=0L;
m.c[1][0]=-1L;m.c[1][1]=0L;m.c[1][2]=1L;
m.c[2][0]=1L;m.c[2][1]=0L;m.c[2][2]=0L;
n-=3;
while(n>0){
if(n%2==1)
t=calone(t,m);
m=calone(m,m);
n/=2;
}
return t.c[0][0];
}
return -1L;
}
Mat calone(Mat a, Mat b)
{
Mat c;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
long long t=1000000007000L;//就是这里
for(int k=0;k<3;k++){
t=(t+a.c[i][k]%M*b.c[k][j]%M)%M;
}
c.c[i][j]=t;
if(c.c[0][0]<0)
cout<<"error"<<endl;
}
}
return c;
}
希望每天进步一点点~~