• CF1106F Lunar New Year and a Recursive Sequence 矩阵快速幂、BSGS


    传送门


    好久没写数论题了写一次调了1h

    首先发现递推式是一个乘方的形式,线性递推和矩阵快速幂似乎都做不了,那么是否能够把乘方运算变成加法运算和乘法运算呢?

    使用原根!学过$NTT$的都知道$998244353$的原根$G=3$。

    使用原根之后,可以得到一个等价的新递推式:(G^{g_i} = prodlimits _ {j=1}^k G^{g_{i - j} imes b_j} mod 998244353(G^{g_i} equiv f_imod 998244353)),它等价于$g_i = sumlimits_k g_ imes b_j mod 998244352$。这样就可以矩阵快速幂得出当$f_k$等于某个值$Gp$时$f_n$的值了。

    可现在知道的是$f_n$的值,不知道$f_k$的值。

    考虑:令$G_k=1$,得到$G_n$的值$x$,那么可以知道$f_k^x equiv f_n mod 998244353$。

    这是一个模意义下的高次剩余方程,要怎么求解呢?

    同样使用原根。设$f_ = Gb mod 998244353$,通过$BSGS$求出$f_n = Gy mod 998244353$,那么原式变成$G^ equiv G^y mod 998244353$,即$bx equiv y mod 998244352$。逆元求解方程得到$b$,也就得到了$f_k$。

    一些打比赛时被坑到的点:

    ①$998244352 = 2^{23} imes 7 imes 17$,求逆元要用欧拉定理或者拓展欧几里得

    ②$998244351 imes 998244351 imes 100 > 2^{63}$,这意味着矩阵相乘不能算完再一起取模

    #include<bits/stdc++.h>
    #define int long long
    //This code is written by Itst
    using namespace std;
    
    inline int read(){
        int a = 0;
        char c = getchar();
        bool f = 0;
        while(!isdigit(c)){
    	    if(c == '-')
    			f = 1;
            c = getchar();
        }
        while(isdigit(c)){
    		a = (a << 3) + (a << 1) + (c ^ '0');
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    
    const int MOD = 998244353 , G = 3;
    int K;
    struct matrix{
    	int a[100][100];
    	int* operator [](int x){return a[x];}
    	matrix(){memset(a , 0 , sizeof(a));}
    	matrix operator *(matrix b){
    		matrix c;
    		for(int i = 0 ; i < K ; ++i)
    			for(int j = 0 ; j < K ; ++j)
    				for(int k = 0 ; k < K ; ++k)
    					c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % (MOD - 1);
    		return c;
    	}
    }S , T;
    
    inline int gcd(int a , int b){
    	int r = a % b;
    	while(r){
    		a = b;
    		b = r;
    		r = a % b;
    	}
    	return b;
    }
    
    inline int poww(int a , int b , int mod = MOD){
    	int times = 1;
    	while(b){
    		if(b & 1)
    			times = times * a % mod;
    		a = a * a % mod;
    		b >>= 1;
    	}
    	return times;
    }
    
    map < int , int > Hash;
    
    inline int BSGS(int x){
    	int t = sqrt(MOD) + 1 , times = x;
        for(int i = 0 ; i < t ; i++){
            Hash[times] = i;
            times = times * G % MOD;
        }
        times = poww(G , t);
        int now = times;
        for(int i = 1 ; i <= t + 1 ; i++){
            if(Hash.count(now)){
                return i * t - Hash[now];
            }
            now = now * times % MOD;
        }
    	return -1;
    }
    
    int phi(int x){
    	int times = x;
    	for(int i = 2 ; i * i <= x ; ++i){
    		if(x % i == 0){
    			times = times / i * (i - 1);
    			while(x % i == 0)
    				x /= i;
    		}
    	}
    	if(x - 1)
    		times = times / x * (x - 1);
    	return times;
    } 
    
    signed main(){
    	#ifndef ONLINE_JUDGE
    	//freopen("in" , "r" , stdin);
    	//freopen("out" , "w" , stdout);
    	#endif
    	K = read();
    	for(int i = 0 ; i < K ; ++i)
    		T[K - i - 1][K - 1] = read() % (MOD - 1);
    	int N = read() - K;
    	int t = BSGS(read());
    	for(int i = 0 ; i + 1 < K ; ++i)
    		T[i + 1][i] = 1;
    	S[0][K - 1] = 1;
    	while(N){
    		if(N & 1)
    			S = S * T;
    		T = T * T;
    		N >>= 1;
    	}
    	int cur = S[0][K - 1] , p = gcd(cur , MOD - 1);
    	if(t % p != 0)
    		puts("-1");
    	else{
    		t /= p;
    		cur /= p;
    		int mod = (MOD - 1) / p; 
    		cout << poww(G , poww(cur , phi(mod) - 1 , mod) * t % mod);
    	}
    	return 0;
    }
    
  • 相关阅读:
    js冒泡排序
    HTML5 canvas 计时器
    centos 6.4安装杀毒软件clamAV 0.98[转]
    PHP大文件下载
    如何在 Eclipse 中使用插件构建 PHP 开发环境[转]
    CentOS 单用户模式:修改Root密码和grub加密[转]
    CentOS 6.0 VNC远程桌面配置[转]
    gprof使用介绍 (gcc -pg) [转]
    VMware NAT端口映射外网访问虚拟机linux
    shell判断文件是否存在[转]
  • 原文地址:https://www.cnblogs.com/Itst/p/10344913.html
Copyright © 2020-2023  润新知