Funny Function
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1192 Accepted Submission(s): 580

For given integers N and M,calculate Fm,1 modulo 1e9+7.
The next T lines,each line includes two integers N and M .
1<=T<=10000,1<=N,M<2^63.
2 2 2 3 3
2 33
题目给出了三个公式。
第一行F[1][1] = F[1][2] = 1;
第一行的其他数F[i][j] = F[i][j-1] + 2*F[i][j-2]。 当j》=3的时候,
当i>=2的时候,F[i][j] 等于i-1行从j开始n个数的和。
给出n,m,求F【m,1】的值。
比赛时画了四张纸,还是没有找出规律,只是发现了n,m的奇、偶性对答案的影响时不同的。看官方题解和
其他人写的式子,我真的退不出来最后的公式,不过在网上发现了一篇文章,就是单纯的去讲他们当时比赛怎么
找规律找出来的,我还勉强可以理解理解。原文作者链接:https://www.dreamwings.cn/hdu6050/4839.html
首先找这种二维数组的规律,我们可以借助Excel表,有格子,比较好看。下面是我用Excel表制的数据
当时我们也写了挺多,就是在纸上写的太乱了,算了后面的数,前面的就丢了,不直观。还是Excel弄出来比较整齐。
或者可以编程计算将数据都写入文件。
然后开始找规律:
当n=2的时候,从第三行开始,发现6/2 = 3 18/6 = 3 54/18 = 3. 我们发现当n=2的时候,F[m][1]/F[m-1][1] = 3 (m>=3)
当n=4的时候,从第三行开始,发现150/10 = 15 2250/150=15.。。。我们发现当n=4的时候,F[m][1]/F[m-1][1] = 15 (m>=3)
这样来看比值3 和 n值2有什么关系, 比值15 和n值4有什么关系, 比较容易发现比值等于2^n-1.
那么规律都是这样的吗。
再来看n为奇数的情况。
然后我们来看当 n n 为奇数的情况,还照之前的做法我们把相邻的两项相除,可以发现这个值无限趋近于 2n−1 ,于是想到会不会相差某一个数字呢。
然后计算
当 n=3..5..7..9 n=3..5..7..9 时,该常数为: 2..10..42..170 2..10..42..170 ,有规律么?
答案当然是有啦~
2 = 2^1
10 = 2^1 + 2^3
42 = 2^1 + 2^3 + 2^5
170 = 2^1 + 2^3 + 2^5 + +2^7
................这个规律真的不太好找。
也就是公比为4,的等比数列的前k项和,公式Sk = a1*(1-q^k)/(1-q),其中首项a1 = 2,公比q = 4,项数k=n/2.将这些值带入上式就能得到,前k
项和。
我们设cha为我们所求得得差,假如F[2][1]时已知得,2^n-1也已知,然后我们如何通过这三者得到F[m][1]呢?
F[3][1] = F[2][1]*(2^n-1) - cha
F[4][1] = F[3][1]*(2^n-1) - cha = F[2][1]*(2^n-1)^2 - cha*(2^n-1) - cha
F[5][1] = F[4][1]*(2^n-1) - cha = F[2][1]*(2^n-1)^3 - cha*(2^n-1)^2 - cha*(2^n-1) - cha
....................
F[m][1] = F[m-1][1]*(2^n-1) - cha = F[2][1]*(2^n-1)^(m-2) - cha*(2^n-1)^(m-3)-.......- cha*(2^n-1)-cha
可以看出后面的 cha*(2^n-1)^(m-3) + cha*(2^n-1)^(m-4) + cha*(2^n-1)^(m-5) + ... + cha*(2^n-1) + cha
把cha提出来后,又是一个等比数列求和,首相为1,公比为(2^n-1),项数为m-2。
则现在是只有F[2][1]不知道了,而F[2][1]又等于第一行前n项的和,那它怎么得到呢,每次给出一个n值,算出第一行
前n个数再求和吗?结果发现F[2][1]也又规律。下面的S[i]为第一行的前i项和,
同样也有规律的,先打表看看~
S[i]: 1 2 5 10 21 42 85 170 341 682 1365 2730 5461 10922 21845 43690 87381 174762 349525
F[i]: 1 1 3 5 11 21 43 85 171 341 683 1365 2731 5461 10923 21845 43691 87381 174763 349525
如果把s[i]整体往后推一位,
S[i]: 1 2 5 10 21 42 85 170 341 682 1365 2730 5461 10922 21845 43690 87381 174762 349525
F[i]: 1 1 3 5 11 21 43 85 171 341 683 1365 2731 5461 10923 21845 43691 87381 174763 349525
当n为奇数的时候,S[n] = F[2][1] = F[1][n+1]
当n为偶数的时候,S[n] = F[2][1] = F[1][n+1] - 1;
所以当给出一个n的时候,只要能算出F[1][n+1],就可以计算出F[2][1]的值。
看别人的博客推出F[1][n+1]也有一个公式F[1][n+1] = ( (-1)^i + 2^(i+1))/3,但是我都看不懂这个式子。
但是之前我用矩阵快速幂求过斐波那契数列,所以我就用矩阵快速幂推导第一行的第n+1项。
首先再第一行我们知道,对于n>=3 Fn = Fn-1 + 2*Fn-2.
则对于n>=3我们可以写出下列矩阵来推导
现在总结公式:
除法过程中取模要用到逆元,因为要取模的那个数时素数,所以可以用小费马定理去求逆元。
AC代码:
#include <iostream>
#include <stdio.h>
using namespace std;
const int mod = 1e9+7;
typedef long long LL;
/*F[1][i] = F[1][i-1] + 2*F[1][i-2],(i>=3)时成立,
又因为F[2][1]等于第一行前n项和相加,当n%2==0.sn = F[1][n+1]-1
当n%2==1,sn = F[1][n+1].所以可以通过矩阵快速幂求F[1][n+1].*/
struct Matrix
{
LL a[4][4];
};
///进行矩阵乘法的函数
Matrix Matrix_Mul(Matrix b,Matrix c)
{
Matrix tmp;
for(int i = 1; i <= 2; i++)
for(int j = 1; j <= 2; j++)
tmp.a[i][j] = 0;
for(int i = 1; i <= 2; i++)
for(int j = 1; j <= 2; j++)
for(int k = 1; k <= 2; k++)
tmp.a[i][j] = ((b.a[i][k]*c.a[k][j])%mod+tmp.a[i][j])%mod;
return tmp;
}
///矩阵快速幂计算F[1][n+1]
LL Matrix_Quick_Pow(LL k)
{
if(k==1 || k==2) return 1;
Matrix ans,res;
res.a[1][1] = 1; res.a[1][2] = 2;
res.a[2][1] = 1; res.a[2][2] = 0;
ans.a[1][1] = 1; ans.a[1][2] = 0;
ans.a[2][1] = 0; ans.a[2][2] = 1;
k = k-2;
while(k)
{
if(k&1)
ans = Matrix_Mul(ans,res);
res = Matrix_Mul(res,res);
k = k>>1;
}
return (ans.a[1][1]+ans.a[1][2])%mod;
}
///整数快速幂函数,求t的k次方
LL Integer_Quick_Pow(LL t,LL k)
{
LL ans,res;
ans = 1;
res = t;
while(k)
{
if(k&1)
ans = (ans*res)%mod;
res = (res*res)%mod;
k = k>>1;
}
return ans;
}
int main()
{
LL n,m,ans;
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
if(m == 1) cout<<"1"<<endl;
else
{
///矩阵快速幂计算F[1][n+1]。
LL Sn = Matrix_Quick_Pow(n+1);
if(n%2==0) Sn = (Sn-1+mod)%mod; ///计算出F[2][1]
LL tmp1=Integer_Quick_Pow(2,n); ///计算2^n
tmp1 = (tmp1-1+mod)%mod; ///计算2^n-1
LL tmp2 = Integer_Quick_Pow(tmp1,m-2); ///计算(2^n-1)^(m-2)
if(n%2==0)
{
ans = (Sn*tmp2)%mod;
}
else
{
LL term = n/2; ///项数
LL tmp3 = Integer_Quick_Pow(4,term); ///4^term
tmp3 = (tmp3-1+mod)%mod; ///4^term-1
LL x = Integer_Quick_Pow(3,mod-2); ///求3的逆元
tmp3 = ((2*tmp3)%mod)*x%mod;
LL y = Integer_Quick_Pow((tmp1-1+mod)%mod,mod-2);
LL tmp4 = (tmp2-1+mod)%mod;
tmp4 = (tmp3*tmp4)%mod*y%mod;
ans = ((Sn*tmp2)%mod-tmp4+mod)%mod;
}
cout<<ans<<endl;
}
}
return 0;
}