2017多校训练第二场 hdu6050 Funny Function(数学+快速幂+逆元)

来源:互联网 发布:java正则表达式$ 编辑:程序博客网 时间:2024/05/20 06:28

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;}