HDU 4979 A simple math problem.

本文介绍了一种使用矩阵快速幂的方法解决大规模递推数列问题的技术。针对给定的分段递推函数,通过构建特定的10×10矩阵并利用快速幂运算,可以有效地计算出f(k)%m的结果,即使k的值非常大也能在合理的时间内得出答案。

A Simple Math Problem
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

Lele now is thinking about a simple function f(x). 

If x < 10 f(x) = x. 
If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10); 
And ai(0<=i<=9) can only be 0 or 1 . 

Now, I will give a0 ~ a9 and two positive integers k and m ,and could you help Lele to caculate f(k)%m. 
 

Input

The problem contains mutiple test cases.Please process to the end of file. 
In each case, there will be two lines. 
In the first line , there are two positive integers k and m. ( k<2*10^9 , m < 10^5 ) 
In the second line , there are ten integers represent a0 ~ a9. 
 

Output

For each case, output f(k) % m in one line.
 

Sample Input

10 9999 1 1 1 1 1 1 1 1 1 1 20 500 1 0 1 0 1 0 1 0 1 0
 

Sample Output

45 104
 

题意
一个分段函数f(x)。
当 x < 10 时,f(x) = x。
当 x >= 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10)。
并且 ai(0<=i<=9) 只能是 0 或 1。
现在给出 a0 ~ a9 ,k 和 m ,输出f(k) % m( k < 2 * 10^9 , m < 10^5 )。

分析
看 k 的范围知道 k 很大,直接跑 o(n) 的循环肯定会 T 掉,所以想到用矩阵来加速,构造的矩阵A为10*10矩阵,如下
 0   1   0   0   0   0   0   0   0   0
 0   0   1   0   0   0   0   0   0   0
 0   0   0   1   0   0   0   0   0   0
 0   0   0   0   1   0   0   0   0   0
 0   0   0   0   0   1   0   0   0   0
 0   0   0   0   0   0   1   0   0   0
 0   0   0   0   0   0   0   1   0   0
 0   0   0   0   0   0   0   0   1   0
 0   0   0   0   0   0   0   0   0   1
a9 a8 a7 a6 a5 a4 a3 a2 a1 a0
所求问题可转化为求由f(0),f(1),...,f(9)构成的列矩阵 F 与 A 的乘法,只需乘 k-9 次即可。

10 * 10 的矩阵看着是真头疼,Orz......

AC代码如下

#include<cstdio>
#include<cstring>
#define N 10
using namespace std;

int k,m;
struct S
{
    int m[N][N];
};

S matrix_multiplication(S a,S b)
{
    S c;
    memset(c.m,0,sizeof(c.m));
    for(int i = 0 ; i < N ; i++)
        for(int j = 0 ; j < N ; j++)
        {
            for(int k = 0 ; k < N ; k++)
                c.m[i][j] += (a.m[i][k] * b.m[k][j]) % m;
            c.m[i][j] %= m;
        }
    return c;
}

S quick_power(S a,int x)
{
    if(x == 1) return a;
    else if(x & 1) return matrix_multiplication(quick_power(a,x-1),a);
    else
    {
        S c = quick_power(a,x/2);
        return matrix_multiplication(c,c);
    }
}

int main()
{
    S a;
    while(~scanf("%d%d",&k,&m))
    {
        for(int i = N-1 ; i >= 0; i--)
            scanf("%d",&a.m[9][i]);
        if(k < 10)
        {
            printf("%d\n",k % m);
            continue;
        }
        for(int i = 0 ; i < N-1 ; i++)
            for(int j = 0 ; j < N ; j++)
            {
                if(j == i + 1) a.m[i][j] = 1;
                else a.m[i][j] = 0;
            }
        S res = quick_power(a,k-9);
        int ans = 0;
        for(int i = 0 ; i < N ; i++)
            ans += (res.m[9][i] * i) % m;
        printf("%d\n",ans%m);
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值