zoukankan      html  css  js  c++  java
  • Codeforces 1106F Lunar New Year and a Recursive Sequence (线性代数、线性递推、数论、BSGS、扩展欧几里得算法)

    哎呀大水题。。我写了一个多小时。。好没救啊。。

    数论板子X合一?

    注意: 本文中变量名称区分大小写。

    题意: 给一个(n)阶递推序列(f_k=prod^{n}_{i=1} f_{k-i}^{b_i}mod P)其中(P=998244353), 输入(b_1,b_2,...,b_n)以及已知(f_1,f_2,...,f_{n-1}=1), 再给定一个数(m)和第(m)项的值(f_m), 求出一个合法的(f_n)值使得按照这个值递推出来的序列满足第(m)项的值为给定的(f_m).

    题解: 首先一个显然的结论是(f_m)可以表示成(prod^{n}_{i=1} f_i^{a_i}), 而且由于(i=1,2,...,n-1)(f_i)的任何次幂都为(1), 因此就是(f_m=f_n^{a}). 令(A(m))(f_m)(f_n)的次数,则有(A[1..n]=[0,0,0,0,...,0,1]), (A_m=sum^{n-1}_{i=1} A(m-i)b_i (m>n)), 即(A)数组满足一个常系数线性递推序列。因此可以用矩阵乘法在(O(n^3log m))的时间内求出(A(m)). 注意因为是指数的运算(((a^n)^m=a^{nm})), 根据费马小定理,这个指数应该模(phi(P)=P-1)而不是(P) (((a^n)^mmod P=a^{nmmod (P-1)}mod P))

    求出来(a=A(m))之后这题就变成了,(f_m=f_n^amod P), 已知(f_m, a), 求出一组合法的(f_n).

    根据常识,(998244353)有原根(3), 我们下文令(G=3) (实际上任何一个原根均可). 设(f_m=G^p, f_n=G^q), 则有(G^pequiv (G^q)^a (mod P)), (pequiv qa(mod P-1)), 然后用BSGS求离散对数(p), exgcd解出(q)就可以了啊……

    时间复杂度(O(sqrt Plog P+n^3log P))

    坑: 注意解同余方程的时候那个(P)的系数不要设成负的。

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<map>
    #define llong long long
    using namespace std;
    
    const int N = 100;
    const int G = 3;
    const int P = 998244353;
    llong quickpow(llong x,llong y)
    {
    	llong cur = x,ret = 1ll;
    	for(int i=0; y; i++)
    	{
    		if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}
    		cur = cur*cur%P;
    	}
    	return ret;
    }
    struct Matrix
    {
    	llong a[N+3][N+3]; int sz1,sz2,sz;
    	void init() {for(int i=1; i<=sz1; i++) for(int j=1; j<=sz2; j++) a[i][j] = 0ll;}
    	Matrix() {}
    	Matrix(int _sz) {sz = sz1 = sz2 = _sz; init();}
    	Matrix(int _sz1,int _sz2) {sz1 = _sz1,sz2 = _sz2; init();}
    	void uinit(int _sz) {sz = sz1 = sz2 = _sz; for(int i=1; i<=sz; i++) for(int j=1; j<=sz; j++) a[i][j] = (i==j)?1:0;}
    	void output() {for(int i=1; i<=sz1; i++) {for(int j=1; j<=sz2; j++) printf("%lld ",a[i][j]); puts("");}}
    };
    Matrix operator *(Matrix x,Matrix y)
    {
    	Matrix ret = Matrix(x.sz1,y.sz2);
    	for(int i=1; i<=x.sz1; i++)
    	{
    		for(int j=1; j<=x.sz2; j++)
    		{
    			for(int k=1; k<=y.sz2; k++)
    			{
    				ret.a[i][j] = (ret.a[i][j]+x.a[i][k]*y.a[k][j])%(P-1ll);
    			}
    		}
    	}
    	return ret;
    }
    Matrix mquickpow(Matrix x,llong y)
    {
    	Matrix cur = x,ret; ret.uinit(x.sz);
    	for(int i=0; y; i++)
    	{
    		if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur;}
    		cur = cur*cur;
    	}
    	return ret;
    }
    namespace BSGS
    {
    	const int B = 31595;
    	map<llong,int> mp;
    	void init()
    	{
    		llong bs = quickpow(G,B); llong j = 1ll;
    		for(int i=0; i<=P; i+=B,j=(j*bs)%P)
    		{
    			mp[j] = i;
    		}
    	}
    	llong Logarithm(llong x)
    	{
    		llong j = 1ll;
    		for(int i=0; i<=B; i++,j=(j*G)%P)
    		{
    			llong tmp = x*j%P;
    			if(mp.count(tmp))
    			{
    				llong ret = (mp[tmp]-i+(P-1))%(P-1);
    				return ret;
    			}
    		}
    		return P-1;
    	}
    }
    Matrix mA,mB,mC;
    llong a[N+3],b[N+3];
    int n; llong m,p,q,lq,lx;
    
    llong gcd(llong x,llong y) {return y==0 ? x : gcd(y,x%y);}
    void exgcd(llong _a,llong _b,llong &_x,llong &_y)
    {
    	if(_b==0ll) {_x = 1ll,_y = 0ll; return;}
    	exgcd(_b,_a%_b,_x,_y);
    	llong tmp = _x; _x = _y; _y = tmp-(_a/_b)*_y;
    }
    llong CongruenceEquation(llong _a,llong _b,llong mod)
    {
    	llong g = gcd(_a,mod),x,y;
    	exgcd(_a/g,mod/g,x,y);
    	return (x*(_b/g)%mod+mod)%mod;
    }
    
    int main()
    {
    	BSGS::init();
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) scanf("%I64d",&b[i]);
    	scanf("%I64d",&m);
    	mA = Matrix(1,n); mA.a[1][1] = 1ll;
    	mB = Matrix(n); for(int i=1; i<n; i++) mB.a[i][i+1] = 1ll; for(int i=1; i<=n; i++) mB.a[i][1] = b[i];
    	mC = mA*mquickpow(mB,m-n); p = mC.a[1][1]; scanf("%I64d",&q);
    	lq = BSGS::Logarithm(q);
    	if(lq%gcd(P-1,p)!=0) {printf("-1
    "); return 0;}
    	lx = CongruenceEquation(p,lq,P-1);
    	llong ans = quickpow(G,lx);
    	printf("%I64d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    POJ2155 Matrix 【二维线段树】
    BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】
    B1027 打印沙漏
    Tomcat无法成功启动——双击startup.bat闪退
    MySQL在cmd命令行查看端口号
    1009 说反话(类似回文字符串)
    除基取余法,
    日期差值
    怎么把VS里的scanf_s换成scanf
    联想小新潮怎么修改fn热键以及怎么进入bios状态
  • 原文地址:https://www.cnblogs.com/suncongbo/p/10352633.html
Copyright © 2011-2022 走看看