zoukankan      html  css  js  c++  java
  • Week14 作业 C

    题目描述:

    Q老师 对数列有一种非同一般的热爱,尤其是优美的斐波那契数列。

    这一天,Q老师 为了增强大家对于斐波那契数列的理解,决定在斐波那契的基础上创建一个新的数列 f(x) 来考一考大家。数列 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 或 1。

    Q老师 将给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。

    聪明的你能通过 Q老师 的考验吗?(k < 2e9, m < 1e5)

    思路:

    • 题目给定的是线性递推式,如果使用普通方法求每一个f(x)则复杂度是O(10*K),无法接受
    • 如何优化呢?至少现在我们能想到,f(n)一定能表示成f(0)...f(9)的函数,如果这样不好理解,可以想到我们的高中学过数列,给定数列的初始项,和递推关系,有时候要求其通项公式,如果得到了通项公式,则求f(x)就是O(1)的,那么问题就转换成了如何求f(x)的通项公式
    • 其实我们可以把数升级到矩阵,每一次递推就相当于一次矩阵运算,多次递推就相当于迭代矩阵运算,具体的,我们可以推导出如下结果:
    • ,矩阵右上角的n-9表示幂次(该图片来自博客https://blog.csdn.net/weixin_44203022/article/details/106330018

    计算矩阵多次幂

    • 借助普通快速幂的思想,我们也可以得到矩阵快速幂,只不过一次相乘的复杂度从O(1)变成了O(N^3),别看O(N^3)很大,主要矛盾在于N不会很大,并且logK也不会很大(logK等于乘法的次数)

    • 具体如何实现:封装矩阵类,重载乘法运算符,动态分配矩阵内存(比静态更灵活),快速幂的单位元变成了单位矩阵

    代码:

    • 之前写的是静态分配数组空间,但是最后期末模拟测试出现了矩阵的大小是由输入的数值决定的,这种情况有两种处理办法:
    • 第一种是动态分配空间,如果这样做,不建议使用memset和memcpy,除非你愿意承担意外的风险
    • 第二种是开很大的矩阵,但是在计算乘法时,传参N(矩阵的大小),只用一部分空间,参数未必放在重载运算符的函数里,可以用一个类变量来标识,使得重载乘法时使用这个变量
    • 下述代码没有内存回收,因为必要性不是很大
    #include <cstdio>
    #include <iostream>
    using namespace std;
    long long K, MOD;
    //这里定义的矩阵其实是方阵,因为行等于列
    struct Matrix
    {
    	long long **x;
    	int N;
    	//Constructor
    	Matrix(int n)	
    	{
    		N = n;
    		x = new long long* [N + 1];
    		for (int i = 1; i <= N; i++)
    			x[i] = new long long[N + 1];
    		//memset 0
    		for (int i = 1; i <= N; i++)
    			for (int j = 1; j <= N; j++)
    				x[i][j] = 0;
    	}
    
    	//可以定义析构函数,但是不是非常有必要
    
    	//operator *
    	Matrix operator*(const Matrix& y) const
    	{
    		Matrix ret(y.N);  //ret==return
    		for (int i = 1; i <= N; ++i)
    			for (int j = 1; j <= N; ++j)
    			{
    				ret.x[i][j] = 0;
    				for (int k = 1; k <= N; ++k)
    					ret.x[i][j] = (ret.x[i][j] + (x[i][k] % MOD) * (y.x[k][j] % MOD) % MOD) % MOD;
    
    			}
    		return ret;
    	}
    
    	//copy Constructor
    	//不建议使用memcpy,因为是动态申请的,对指针memset或memcpy可能出问题
    	Matrix(const Matrix& t)  
    	{
    		N = t.N;
    		x = new long long* [N + 1];
    		for (int i = 1; i <= N; i++)
    			x[i] = new long long[N + 1];
    		for (int i = 1; i <= N; i++)
    			for (int j = 1; j <= N; j++)
    				x[i][j] = t.x[i][j];
    	}
    };
    Matrix mqpow(Matrix a, int x)
    {
    	Matrix ret(a.N);
    	//置为单位矩阵
    	for (int i = 1; i <= ret.N; i++)
    		ret.x[i][i] = 1;
    	//迭代矩阵快速幂
    	while (x)
    	{
    		if (x & 1) ret = ret * a;
    		a = a * a;
    		x >>= 1;
    	}
    	return ret;
    }
    int main()
    {
    	while (cin >> K >> MOD)
    	{
    		Matrix A(10);
    		for (int i = 1; i <= 10; i++)
    			cin >> A.x[1][i];
    		for (int i = 2; i <= 10; i++)
    			A.x[i][i - 1] = 1;
    		Matrix HalfAns = mqpow(A, K - 9);
    		long long FinalAns = 0;
    		for (int i = 1; i <= 10; i++)
    			FinalAns = (FinalAns + HalfAns.x[1][i] * (10LL - i) % MOD) % MOD;
    		cout << FinalAns << endl;
    	}
    	return 0;
    }
  • 相关阅读:
    构建TensorFlow数据流图
    Python小练习:复制操作
    Python小练习:列表的相关操作
    【Jave】接入极光推送 ------- 封装极光推送工具类
    jenkins邮件-使用变量定制化html邮件报告
    十六进制的颜色转变为rgb,设置透明度,通用方法
    一. Go微服务--隔离设计
    7.23 学习笔记
    7.22 学习笔记
    8.28正睿CSP七连测day1
  • 原文地址:https://www.cnblogs.com/qingoba/p/13110730.html
Copyright © 2011-2022 走看看