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^3\log m)\)的时间内求出\(A(m)\). 注意因为是指数的运算(\((a^n)^m=a^{nm}\)), 根据费马小定理,这个指数应该模\(\phi(P)=P-1\)而不是\(P\) (\((a^n)^m\mod P=a^{nm\mod (P-1)}\mod P\))

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

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

时间复杂度\(O(\sqrt P\log P+n^3\log 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\n"); return 0;}
    lx = CongruenceEquation(p,lq,P-1);
    llong ans = quickpow(G,lx);
    printf("%I64d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/suncongbo/p/10352633.html

时间: 2024-11-06 07:43:27

Codeforces 1106F Lunar New Year and a Recursive Sequence (数学、线性代数、线性递推、数论、BSGS、扩展欧几里得算法)的相关文章

Codeforces 536F Lunar New Year and a Recursive Sequence | BSGS/exgcd/矩阵乘法

我诈尸啦! 高三退役选手好不容易抛弃天利和金考卷打场CF,结果打得和shi一样--还因为queue太长而unrated了!一个学期不敲代码实在是忘干净了-- 没分该没分,考题还是要订正的 =v= 欢迎阅读本题解! P.S. 这几个算法我是一个也想不起来了 TAT 题目链接 Codeforces 536F Lunar New Year and a Recursive Sequence 新年和递推数列 题意描述 某数列\(\{f_i\}\)递推公式:\[f_i = (\prod_{j=1}^kf_{

CF1106F Lunar New Year and a Recursive Sequence(矩阵快速幂+bsgs+exgcd)

题面 传送门 前置芝士 \(BSGS\) 什么?你不会\(BSGS\)?百度啊 原根 对于素数\(p\)和自然数\(a\),如果满足\(a^x\equiv 1\pmod{p}\)的最小的\(x\)为\(p-1\),那么\(a\)就是\(p\)的一个原根 离散对数 对于素数\(p\),以及\(p\)的一个原根\(g\),定义\(y\)为\(x\)的离散对数,当且仅当\(g^y\equiv x\pmod{p}\),记\(y\)为\(ind_g x\).不难发现原数和离散对数可以一一对应.也不难发现离

CF1106F Lunar New Year and a Recursive Sequence

又傻掉了呢 看到连乘显然直接转原根变成线性齐次递推式. 矩阵乘法求一发. 然后分析一下发现是个x^k=m的形式. 按照套路解一下高次方程就好了. 需要用到exgcd和bsgs. #include<iostream> #include<cctype> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<ctime> #inc

CF1106F Lunar New Year and a Recursive Sequence——矩阵快速幂&amp;&amp;bsgs

题意 设 $$f_i = \left\{\begin{matrix}1 , \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \  i < k\\ \prod_{j=1}^k f_{i-j}^{b_j} \ mod \ p, \ \ \ \ \ i > k\end{matrix}\right.$$ 求 $f_k$($1 \leq f_k < p$),使得 $f_m = n$.($1 \leq k\leq 100$) 分析 $f_n$ 可以表示

HDu 5950 Recursive sequence(矩阵快速幂)

Recursive sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1323    Accepted Submission(s): 589 Problem Description Farmer John likes to play mathematics games with his N cows. Recently,

HDU 5950 Recursive sequence 【递推+矩阵快速幂】 (2016ACM/ICPC亚洲区沈阳站)

Recursive sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 249    Accepted Submission(s): 140 Problem Description Farmer John likes to play mathematics games with his N cows. Recently, t

Codeforces Round #260(div2)C(递推)

有明显的递推关系: f[i]表示i为数列中最大值时所求结果.num[i]表示数i在数列中出现了几次. 对于数i,要么删i,要么删i-1,只有这两种情况,且子问题还是一样的思路.那么很显然递推一下就行了:f[i]=max(f[i-1],f[i-2]+i*num[i]); 这里技巧在于:为了防止麻烦,干脆就所有数的出现次数都记录一下,然后直接从2推到100000(类似于下标排序),就不用排序了,也不用模拟删除操作了.这一技巧貌似简单,但实际上临场想出来也需要点水平. #include<iostrea

codeforces 735C Tennis Championship(贪心+递推)

Tennis Championship 题目链接:http://codeforces.com/problemset/problem/735/C --每天在线,欢迎留言谈论. 题目大意: 给你一个 n (2≤n≤10^18),代表一共有n位参加比赛的选手. 游戏规则: ①每次比赛,输的选手将离开赛场 ②相互比赛的选手 他们的获胜的次数相差不能超过1(获胜4次的选手只能跟3或5次的选手比赛) 问题:最终赢得比赛的选手,胜场最多能为多少. 思路: 贪心:①选一名选手让他一直获胜且优先让他参加比赛 ②当

递推 Codeforces Round #186 (Div. 2) B. Ilya and Queries

题目传送门 1 /* 2 递推:用cnt记录前缀值,查询区间时,两个区间相减 3 */ 4 #include <cstdio> 5 #include <algorithm> 6 #include <cmath> 7 #include <cstring> 8 using namespace std; 9 10 const int MAXN = 1e5 + 10; 11 const int INF = 0x3f3f3f3f; 12 char s[MAXN]; 1