A Short problem ( 矩阵快速幂&&循环节)

According to a research, VIM users tend to have shorter fingers, compared with Emacs users.
  Hence they prefer problems short, too. Here is a short one:

Given n (1 <= n <= 10^18), You should solve for
g(g(g(n))) mod 10 9 + 7
g(n) = 3g(n - 1) + g(n - 2)
g(1) = 1
g(0) = 0

Input
  There are several test cases. For each test case there is an integer n in a single line.
  Please process until EOF (End Of File).
Output
  For each test case, please print a single line with a integer, the corresponding answer to this case.

Sample Input

0
1
2

Sample Output

0
1
42837

中间矩阵很好求

g(n) = 3g(n - 1) + g(n - 2)
[ g(1) ] * [3 1] = [ g(2) ]
[ g(0) ]   [1 0]   [ g(1) ]

[ g(1) ] * [3 1] ....  * [3 1]  (乘了n-1个)   = [ g(n)   ]
[ g(0) ]   [1 0]         [1 0]                 [ g(n-1) ]

若【3 1】的n-1次方为【a b】
  【1 0】          【c d】
则g(n) = a*g(1) + b*g(0) = a;

求矩阵幂就行了。
主要是mod的值,题给的是 g(g(g(n))) mod 10^9 + 7,意思是三层mod是10^9 + 7,
里面两层呢???

这就是循环节,举个例子。
a(t) % (10^9 + 7) = 6
a(t+1) % (10^9 + 7) = 7
a(t+2) % (10^9 + 7) = 8
a(t+2) % (10^9 + 7) = 9

a(t+x) % (10^9 + 7)=6
这就看出来规律了,a(t+nx) %(10^9 + 7) = 6;
为了防止括号里面的值爆掉,我们只需要让里面的值%x就行了,这样就不会爆掉。

让x1, x2, x3表示三次的mod吧。
x3 = 10^9 + 7;
int main()
{
   ll u = 1, v = 0;
   for(ll i = 1; i <= 111111111111; i++)     //这里的i表示就是g(i)到那了。
   {
      ll t = ((3*u)%x3 + v%mod) % x3;      //  这里是x3
      v = u;
      u = t;
      if(u == 1 && v == 0) 
      {
      x2 = i;
      break;
      }
   }
   return 0;
}
这里就求出来一个g( g(g(n)) % x2) % x3;

再求一次
int main()
{
   ll u = 1, v = 0;
   for(ll i = 1; i <= 111111111111; i++)     //这里的i表示就是g(i)到那了。
   {
      ll t = ((3*u)%x2 + v%mod) % x2;      //  这里是x2
      v = u;
      u = t;
      if(u == 1 && v == 0) 
      {
      x2 = i;
      break;
      }
   }
   return 0;
}

这里就求出来一个 g( g( g(n) % x1 ) % x2) % x3; 让我们求的就是这个,这两个解得先写个函数算出来,写这个题直接用,x2 = 222222224; x1 = 183120; 要不然光算这就超时了。上代码

#include<stdio.h>
#include<string.h>
#include<cmath>
#include<cstdlib>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
const int N = 4;
using namespace std;
struct node
{
   ll m[N][N];
}a, res, tmp;
node zhen(node x, node y, ll mod)
{
    for(int i = 1; i <= 2; i++)
    for(int j = 1; j <= 2; j++)
    tmp.m[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.m[i][j] = (tmp.m[i][j] % mod + ((x.m[i][k] % mod)*(y.m[k][j] % mod))%mod) % mod;
    return tmp;
}
void p(node a, ll n, ll mod)
{
    for(int i = 1; i <= 2; i++)
    for(int j = 1; j <= 2; j++)
    res.m[i][j] = 0;
    res.m[1][1] = res.m[2][2] = 1;
    while(n)
    {
    if(n&1)
    res = zhen(res, a, mod);

    a = zhen(a, a, mod);

    n>>=1;
    }
}
void gg()          //初始
{
a.m[1][1] = 3;
a.m[1][2] = 1;
a.m[2][1] = 1;
a.m[2][2] = 0;
}
int main()
{
    ll mod = 1000000007;
    ll t;
    ll x1 = mod;
    ll x2 = 222222224;
    ll x3 = 183120;
    while(cin >> t)
    {
    if(t == 1 || t == 0)
    {
    cout << t << endl;
    continue;
    }
    gg();
    p(a, t-1, x3);
    t = res.m[1][1]%x3;
    if(t == 1 || t == 0)
    {
    cout << t << endl;
    continue;
    }
    gg();
    p(a, t-1, x2);
    t = res.m[1][1]%x2;
    if(t == 1 || t == 0)
    {
    cout << t << endl;
    continue;
    }
    gg();
    p(a, t-1,x1);
    t = res.m[1][1]%x1;
    cout << t << endl;
    }
    return 0;
}

“这些年我一直提醒自己一件事情:千万不要自己感动自己。大部分人看似的努力不过是愚蠢导致的,什么熬夜看书到天亮,连续几天就睡几个小时,多久没放假了等等。如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多,人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里 。”
——于宙 《我们这一代人的困惑》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值