2017多校训练第二场 hdu6050 Funny Function(数学+快速幂+逆元)
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
Problem Description
Function Fx,y satisfies:
For given integers N and M,calculate
Fm,1 modulo 1e9+7.
Input
There is one integer T in the first line.
The next T lines,each line includes two integers N and M .
1<=T<=10000,1<=N,M<2^63.
Output
For each given N and M,print the answer in a single line.
Sample Input
2
2 2
3 3
Sample Output
2
33
样例理解:
2(T)个测试数据,每个数据给你2(N),2(M),求出Fm1的值,计算方法如图。
那么我们可以按照题目意思列出以下表格:
当N=2,M=2时:
F11=1 F12=1
F21=(F11+F12)=2
所以第一组测试样例的值为F21=2
当N=3,M=3时:
F11=1 F12=1 F13=3 F14=5 F15=11
F21=(F11+F12+F13)=5 F22=(F12+F13+F14)=9 F23=(F13+F14+F15)=19
F31=(F21+F22+F23)=33
所以第一组测试样例的值为F31=33
分析:这道题可以直接用矩阵快速幂做,不过公式也是很好推导的。
根据高中数学里有关数列的知识,
题目中所给式子 F1i=F1i-1+2*F1i-2,
可以化成 F1j-2*F1j-1=F1j-1-2*F1j-2
那么可以计算出F1j的通向公式:F1j=1/3*(2^n-(-1)^n)
计算出这个通项公式以后,我们可以把数列F分成2^n和(-1)^n两个部分,1/3可以最后再处理
那么F1就变成了这样的:
2^1 2^2 2^3 2^4… -1 1 -1 1 -1 1
而F21是F11前N项求和,利用等比数列求和公式,得到F21=(2^n-1)*2
而我们发现,F22是从F12开始的N项求和,它的每一项F1i都是F1i-1的两倍,所以F22是F21的两倍,以此类推,我们可以把F2n的左边写出来:
(2^n-1)2 (2^n-1)(2^2) (2^n-1)*(2^3) …
而F2n的右边的值取决于N的值,我们发现当N为偶数时右边的和为0,N为奇数时右边的和为-1
所以F2n的右边如下,且当M>=2时,它没有变化:
-1 0 -1 0 -1 0 -1 0…
往下推F3n的时候,我们发现,F2的左边又是一个等比数列,
用等比数列求和公式发现F31=(2^n-1)^2*2
,我们发现F31比F21多了一个(2^n-1),F21比F1多了一个(2^n-1),而且这个是不断递增下去的
那么第M行,F的左边应该是(2^n-1)^(M-1)*2,当N为奇数的时候,别忘了再+1(即-(-1)),还有之前先放下的系数1/3,就得到了最后的通项公式:
Fm1=1/3*(2^n-1)^(M-1)*2 (当N是偶数)
1/3*[(2^n-1)^(M-1)*2+1] (当N是奇数)
实现技巧:由于N,M的范围非常大(2^63),所以我们采用快速幂的办法迅速算出a^b,还有一个要注意的点是1/3这个系数,在mod1e9+7的情况下是不可以直接把答案除3的,应该乘以3关于1e9+7的逆元(此处等于333333336是我们为了节约时间提前算好的),我和队友当时直接除了3,导致WA了一次,还是很可惜
AC代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <iomanip>
#include <vector>
#include <queue>
#include <map>
#include <cstring>
#include <string>
#include <cctype>
#include <cmath>
typedef long long LL;
using namespace std;
const LL modd=1e9+7;
LL niyuan = 333333336;
//快速幂模板
LL quickmod(LL a,LL b)
{
LL ans = 1;
while(b) //用一个循环从右到左便利b的所有二进制位
{
if(b&1) //判断此时b[i]的二进制位是否为1
{
ans = (ans*a)%modd; //乘到结果上,这里a是a^(2^i)%m
b--; //把该为变0
}
b/=2;
a = a*a%modd;
}
return ans;
}
int main()
{
int cas;
LL N,M;
cin >> cas;
while(cas--)
{
cin >> N >> M;
LL res = quickmod(2,N) - 1;
M--;
res = quickmod(res,M) * 2 % modd;
if(N % 2 == 1) res += 1;
res= res * niyuan % modd;
cout << res<< endl;
}
return 0;
}