递推式子+矩阵快速幂+欧拉降幂

题目描述

百度之星2022年题——离散数学 题目链接
在这里插入图片描述

题目大致解析

  • 大致解题思路
  • 看到题目的n值如此之大,要么就是有结论,要么就是log级别的算法,而这种算方案数的一种最经典方式就是dp,dp中的log级别一般就是使用了矩阵快速幂加速。

递推式子

  1. 寻找f[n]和f[n - 1]的直接关系(有时甚至还要考虑f[n - 2], f[n - 3]等):得到0的情况很少,所以我们不妨先找到能够产生0的情况,再用总的选择数减去它即可。要使得n个数的情况能为0,则一个充分条件为【前n-1个数满足两个蕴含条件,且第n个数为0】,因为这样将导致第一个蕴含条件不满足;而事实上,这个条件也是一个必要条件,证明略过。
    f [ n ] = 2 n − f [ n − 1 ] f[n] = 2^n - f[n - 1] f[n]=2nf[n1]
  2. 只有线性的式子才能被矩阵快速幂加速(因为要乘的矩阵的各元素均是常数),所以要进行转换,得到如下式子:
    f [ n ] = f [ n − 1 ] + 2 ∗ f [ n − 2 ] f[n] = f[n - 1] + 2 * f[n - 2] f[n]=f[n1]+2f[n2]
  3. 右边有两类f,左边有一类f,不好设计矩阵,故增加如下的一个式子:
    f [ n − 1 ] = f [ n − 1 ] f[n - 1] = f[n - 1] f[n1]=f[n1]
  4. 将上面两个式子用矩阵表示,如下所示, 矩阵递推步长为1:
    [ f [ n ] f [ n − 1 ] ] = [ f [ n − 1 ] f [ n − 2 ] ] [ 1 1 2 0 ] \left[ \begin{matrix} f[n] & f[n - 1]\\ \end{matrix} \right] = \left[ \begin{matrix} f[n -1] &f[n -2]\\ \end{matrix} \right] \left[ \begin{matrix} 1 & 1 \\ 2 & 0 \\ \end{matrix} \right] [f[n]f[n1]]=[f[n1]f[n2]][1210]

欧拉降幂

  • 相关公式如下,素数的欧拉函数为素数减一在这里插入图片描述
  • 不太确定矩阵乘方是否能用欧拉降幂,暂时没有看到比较严格的证明,有大佬懂的话可以在评论区发一下,我这个做法也只是搬运别人的。

相关代码

  • 个人认为本人的这个矩阵乘法和快速幂的板子还是蛮好用的
#include <iostream>
#include <stack>
#include <map>
#include <queue>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <string.h> 
using namespace std;
typedef long long ll;
#define int ll
const ll mod = 998244353;//不需要模的时候,把模数调大点即可 
typedef struct Mat{
    vector<vector<int>> m;
	int h, wide; 
    Mat(vector<vector<int>> &tmp){
    	m = tmp;
		h = m.size(), wide = m[0].size();
	};
	Mat(int h, int wide, int x = 0){
		m.resize(h);
		for (int i = 0; i < h; i++){
			m[i].resize(wide);
			for (int j = 0; j < wide; j++){
				m[i][j] = x;
			}
		}
		this -> h = h, this -> wide = wide;
	};
}Mat;//存储结构体
Mat mat_mul(Mat x,Mat y)
{
    Mat c(x.h, y.wide);
    for(int i = 0; i < x.h; ++i){
        for(int j = 0; j < y.wide; ++j){
            for(int k = 0; k < y.h; ++k){
                c.m[i][j] = (c.m[i][j] + x.m[i][k] * y.m[k][j] % mod) % mod;//可选择更换为其他的求矩阵元素的操作 
            }
        }
    }
    return c;
}
Mat mat_pow(Mat x, ll y)//矩阵快速幂
{
    Mat ans(x.h, x.h);
    for (int i = 0; i < ans.h; i++){//要初始化为1矩阵,注意不是全1! 
    	ans.m[i][i] = 1;
	}
    while(y){
        if(y & 1) ans = mat_mul(ans, x);
        x = mat_mul(x, x);
        y >>= 1;
    }
    return ans;
}
vector<vector<int>> a = {{1, 1}, {2, 0}};//一些初始矩阵可以这样初始化
vector<vector<int>> f = {{3, 1}};
void solve(){
    string s;
    cin >> s;
    int n = 0;
    for(int i = 0 ; s[i] ; i ++ )
        n = (n * 10 + s[i] - '0') % (mod - 1);
    n = (n - 2 + mod - 1) % (mod - 1);
    Mat a_mat(a);
	Mat f_mat(f);
	Mat ans(mat_mul(f, mat_pow(a, n)));
	cout << ans.m[0][0] << endl;
}

signed main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); 
	std::cout.tie(0);
	int T = 1;
	while(T--){
		solve();
	}
	return 0;
} 

  • 44
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值