• @bzoj



    @description@

    Fib数列为1,1,2,3,5,8...
    求在Mod10^9+9的意义下,数字N在Fib数列中出现在哪个位置
    无解输出-1

    原题传送门。

    @solution@

    一个熟练的 OIer 选手应该能迅速发现 5 在模 10^9 + 9 意义下有二次剩余。考虑斐波那契通项公式:

    [f_i = frac{1}{sqrt{5}}((frac{1 + sqrt{5}}{2})^n - (frac{1 - sqrt{5}}{2})^n) = N ]

    不妨记 (a = frac{1 + sqrt{5}}{2}, b = frac{1 - sqrt{5}}{2}),则我们要解方程 (a^n - b^n = sqrt{5}N = K)

    这个方程可解吗?注意 a, b 存在关系:(a + b = 1, ab = -1)。前一个显然不好用,我们用后一个:

    [a^n - (frac{-1}{a})^n = K\ a^{2n} - Ka^{n} - (-1)^n = 0\ a^n = frac{K pm sqrt{K^2 + 4 imes(-1)^n}}{2} ]

    讨论一下 n 的奇偶可以解出 (a^n),然后 BSGS 即可。平方根也可 BSGS + 原根来解。

    @accepted code@

    #include <cstdio>
    #include <vector>
    #include <cassert>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int MOD = int(1E9) + 9;
    const int INV2 = (MOD + 1) / 2;
    const int INV5 = (MOD + 1) / 5;
    const int SQ5 = 383008016;
    const int A = 1LL*(MOD + 1 + SQ5)*INV2%MOD;
    const int B = 1LL*(MOD + 1 - SQ5)*INV2%MOD;
    const int C = 1LL*SQ5*INV5%MOD;
    const int G = 13;
    const int BLOCK = 32000;
    const int HASHSIZE = 1000037;
    const int MA = 133086171;
    const int INVMA = 74832817;
    const int GCD = 3;
    
    inline int add(int x, int y) {return (x + y >= MOD ? x + y - MOD : x + y);}
    inline int sub(int x, int y) {return (x - y < 0 ? x - y + MOD : x - y);}
    inline int mul(int x, int y) {return 1LL * x * y % MOD;}
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	for(int i=p;i;i>>=1,b=mul(b,b))
    		if( i & 1 ) ret = mul(ret, b);
    	return ret;
    }
    int fib(int n) {
    	int x = sub(pow_mod(A, n), pow_mod(B, n));
    	return mul(C, x);
    }
    
    vector<pair<int, int> >h[HASHSIZE];
    int hash_search(int x) {
    	int y = x % HASHSIZE;
    	for(int i=0;i<h[y].size();i++)
    		if( h[y][i].first == x )
    			return h[y][i].second;
    	return -1;
    }
    void hash_insert(int x, int k) {
    	int y = x % HASHSIZE;
    	for(int i=0;i<h[y].size();i++)
    		if( h[y][i].first == x )
    			return ;
    	h[y].push_back(make_pair(x, k));
    }
    void init() {
    	int p = pow_mod(G, BLOCK);
    	for(int i=1,q=p;i<=BLOCK;i++,q=mul(q,p))
    		hash_insert(q, i*BLOCK);
    }
    int bsgs(int x) {
    	if( x == 0 ) return -1;
    	int ret = MOD;
    	for(int i=1,q=G;i<=BLOCK;i++,q=mul(q,G)) {
    		int p = hash_search(mul(x, q));
    		if( p != -1 ) ret = min(ret, p - i);
    	}
    	assert(pow_mod(G, ret) == x);
    	return ret;
    }
    int msqrt(int x) {
    	if( x == 0 ) return 0;
    	int p = bsgs(x); 
    	return p % 2 ? -1 : pow_mod(G, p / 2);
    }
    
    int ans = -1;
    void update(int x, int r) {
    	x = 1LL*(x / GCD)*INVMA%((MOD - 1) / GCD);
    	if( x % 2 == r )
    		if( ans == -1 || ans > x )
    			ans = x;
    }
    int main() {
    	int N, K; scanf("%d", &N), K = mul(N, SQ5), init();
    	
    	int P = msqrt(add(mul(K, K), 4)); // even
    	if( P != -1 ) {
    		int k = bsgs(mul(add(K, P), INV2));
    		if( k != -1 && k % GCD == 0 ) update(k, 0);
    		k = bsgs(mul(sub(K, P), INV2));
    		if( k != -1 && k % GCD == 0 ) update(k, 0);
    	}
    	
    	P = msqrt(sub(mul(K, K), 4)); // odd
    	if( P != -1 ) {
    		int k = bsgs(mul(add(K, P), INV2));
    		if( k != -1 && k % GCD == 0 ) update(k, 1);
    		k = bsgs(mul(sub(K, P), INV2));
    		if( k != -1 && k % GCD == 0 ) update(k, 1);
    	}
    	
    	if( ans != -1 ) assert(fib(ans) == N);
    	printf("%d
    ", ans);
    }
    

    @details@

    因为几乎都是常数,可以预处理出来直接用。

    话说这道题的主要难点是解方程那一块吧,感觉数学味儿多一点。

  • 相关阅读:
    mysql操作规范
    在线修改大表结构pt-online-schema-change
    MySQL 过滤复制
    IDEA “Cannot resolve symbol” 解决办法
    JAVA NIO Selector Channel
    Mysql二级索引
    技术选型关于redis客户端选择
    知乎上看到的一篇讲解Epoll的文章,较形象生动
    linux安装运行virtuoso数据库的详细过程
    go安装步骤(linux和Windows)
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12445554.html
Copyright © 2020-2023  润新知