zoukankan      html  css  js  c++  java
  • 递推式的循环问题

    形如 斐波拉契数列 这样的形形色色的递推式 模上一个数M过后的值 是一个循环的效果。

    可以这样解释 拿斐波拉契数列举例 F[i] = F[i - 1] + F[i - 2] ;F[i] 如果模上一个数M可以得到M个结果, 并且是由前两项转移过来的, 那么最多有 M ^ 2 项会出现循环,但往往是小于M ^ 2 的,如果是由前三项转移的话,那么最多经过M ^ 3项后出现循环

    套路一:

    数据允许的范围求循环节

    给出两个非整数a, b, n(0  <= a, b <= 2^64, 1 <= n <= 1000),任务是计算出f(a^b) % n, 其中f[0] = f[1] = 1, f[i] = f[i - 1] + f[i - 2]

    由上面知道最多会有 n ^ 2 项会出现循环,判断出现循环的方法:连续出现 (f[0] : 1, f[1] : 1),因为循环的实质是前两项出现循环,那么方法就很明显了,首先找到关于数列模上n的循环节长度,然后用快速幂计算a ^ b % n,然后就知道a ^ b在一个循环节的哪一项了。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll unsigned long long
    
    const int N = 1e6 + 7;
    ll a, b, n;
    int kase, F[N], M;
    
    ll readll () {
    	ll K = 0; char c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') K = K * 10 + c - '0',c = getchar();
    	return K;
    }
    
    int pow (ll x,ll y){
    	ll ret = 1;
    	while (y) {
    		if(y & 1) ret = (x * ret) % M;
    		x = (x % M) * (x % M) % M;
    		y >>= 1;
    	}
    	return ret;
    }
    
    int main(){
    	scanf ("%d", &kase);
    	while (kase--) {
    		a = readll(), b = readll(), n = readll();
    		if( !a || n==1 ){puts("0");continue;}
    		F[0] = F[1] = 1;
    		int cur = 2,flag = 0;
    		while (true) {
    			F[cur] = (F[cur-1] + F[cur-2]) % n;
    			if (F[cur] != 1) flag = 0;
    			else if (F[cur] == 1 && flag == 0) flag = 1;
    			else if (F[cur] == 1 && flag == 1) break;
    			++cur;
    		}
    		M = cur - 1;
    		printf("%d
    ", F[(pow(a, b) - 1 + M) % M]);
    	}
    	return 0;
    }
    

      

    套路二

    对于特定的模打表找循环节

    依然拿斐波拉契数列举例,给出一个数字n (<=10^1000),求f[f[i]] % 1e9+7 的数值

    对于f[x] % M 项的数值最坏情况下会出现M ^ 2的循环,但是肯定要找循环节,所以打一个表碰运气看他的循环是多少,根据实践循环节的长度为2e9 + 16,那么在里面的f[i]就模上2e9 + 16,现在转换为f[x] % 2e9 + 16的问题了,一样的打表找循环节,长度为329616,那么n就可以直接模上329616,剩下的就是矩阵的事情了~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define ll long long
    char ch[1050];
    int mod[3];
    struct Matrix {
    	ll map[3][3];
    	void clear () {memset(map, 0, sizeof (map));}
    };
    
    Matrix Mul (Matrix x, Matrix y, int F) {
    	Matrix ret;
    	ret.clear();
    	for (int i = 0; i < 2; ++i) 
    		for (int j = 0; j < 2; ++j)
    			for (int k = 0; k < 2; ++k)
    				ret.map[i][j] = (ret.map[i][j] + x.map[i][k] * y.map[k][j] % mod[F]) % mod[F];
    	return ret;
    }
    
    Matrix Pow (Matrix x, int n, int F) {
    	Matrix ret;
    	ret.clear();
    	ret.map[0][0] = 1;
    	ret.map[1][1] = 1;
    	while (n) {
    		if (n & 1) ret = Mul(ret, x, F);
    		x = Mul(x, x, F);
    		n >>= 1;
    	}
    	return ret;
    }
    
    int main () {
    	int kase;
    	mod[1] = 2e9 + 16, mod[2] = 1e9 + 7, mod[3] = 329616;
    	scanf ("%d", &kase); 
    	while (kase--) {
    		ll n = 0;
    		scanf ("%s", &ch);
    		int len = strlen(ch);
    		for (int i = 0; i < len; ++i) n = (n * 10 % 329616 + ch[i] - '0') % 329616;
    		Matrix ans, ori;
    		ans.clear(), ori.clear();
    		ans.map[0][0] = 0;
    		ans.map[0][1] = 1;
    		ori.map[0][0] = 0;
    		ori.map[0][1] = 1;
    		ori.map[1][0] = 1;
    		ori.map[1][1] = 1;
    		ans = Mul(ans, Pow(ori, n, 1), 1);
    		n = ans.map[0][0];
    		ans.map[0][0] = 0;
    		ans.map[0][1] = 1;
    		ans = Mul(ans, Pow(ori, n, 2), 2);
    		printf ("%d
    ", ans.map[0][0]);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    Android常用开发工具的用法
    搭建Android开发环境
    开篇 Android系统的体系结构
    学习安卓笔记
    C# DllImport用法和路径问题
    jq 实现无限级地区联动 样式为bootstrap
    YII2 日志
    centos6.5 lamp 环境 使用yum安装方法
    mysql 时间戳 按周、日、月 统计方法 附 date格式
    Yii2.0中文开发向导——控制器(Controller)
  • 原文地址:https://www.cnblogs.com/xgtao/p/5964720.html
Copyright © 2011-2022 走看看