Codeforces 1106F Lunar New Year and a Recursive Sequence | BSGS/exgcd/矩阵乘法
我诈尸啦!
高三退役选手好不容易抛弃天利和金考卷打场CF,结果打得和shi一样……还因为queue太长而unrated了!一个学期不敲代码实在是忘干净了……
没分该没分,考题还是要订正的 =v= 欢迎阅读本题解!
P.S. 这几个算法我是一个也想不起来了 TAT
题目链接
Codeforces 1106F Lunar New Year and a Recursive Sequence 新年和递推数列
题意描述
某数列\(\{f_i\}\)递推公式:$$f_i = (\prod_{j=1}kf_{i-j}{b_j}) \bmod p$$
其中\(b\)是已知的长度为\(k\)的数列,\(p = 998244353\),\(f_1 = f_2 = ... = f_{k-1} = 1\),\(f_k\)未知。
给出两个数\(n, m\),构造一个\(f_k\)使得\(f_n = m\),无解输出-1。
\(k \le 100, n \le 10^9\)
题解
数论!真令人头秃!
首先这个数据范围让人想到什么?矩阵乘法!
矩阵乘法想推这个全是乘法和乘方的递推数列咋办?取对数!离散对数!
于是这道题关键的两个考点就被你发现啦!
(然而我太菜了,并不能发现 = =)
什么是离散对数?
众所周知(众==学过NTT的人等),这个喜闻乐见的模数\(p = 998244353\)有个原根\(g=3\),\(g^i(0\le i < P - 1)\)和\(1\le x < P\)一一对应。那么类比我们学过的对数,称这个\(i\)为\(x\)的离散对数。
令数列\(h_i\)为\(f_i\)的离散对数。
那么有递推式:$$h_i = (\sum_{j=1}^kb_j\cdot h_{i-j}) \bmod (p - 1)$$
其中\(h_1 = h_2 = ... = h_{k-1} = 0\)。注意模数变成了\(p - 1\)(费马小定理)。
这个就可以用矩阵加速了!如果我们把\(h_k\)设为1带进去,求得\(h_n = c\),那么有\(h_n = c \cdot h_k \bmod (p - 1)\);
\(h_n\)即为\(m\)的离散对数,用BSGS可求;
exgcd解刚才这个同余方程即可得到\(h_k\);
\(f_k = g^{h_k}\),快速幂即可得到\(f_k\)。
如果exgcd发现没有解的话就输出-1。
是不是思路非常清晰啊~
代码
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cassert>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 102, P = 998244353, P2 = 998244352, G = 3;
int K;
ll b[N], n, m, C;
namespace BSGS {
    const int S = 32000, M = 2000000;
    int cnt = 0, adj[M + 5], nxt[S + 5];
    ll key[S + 5], val[S + 5];
    void insert(ll K, ll V){
        int p = K % M;
        key[++cnt] = K;
        val[cnt] = V;
        nxt[cnt] = adj[p];
        adj[p] = cnt;
    }
    ll search(ll K){
        for(int u = adj[K % M]; u; u = nxt[u])
            if(key[u] == K) return val[u];
        return -1;
    }
    void init(){
        ll sum = 1;
        for(int i = 1; i <= S; i++)
            sum = sum * G % P;
        ll tot = 1;
        for(int i = 1; (i - 1) * S < P - 1; i++)
            tot = tot * sum % P, insert(tot, i * S);
    }
    ll log(ll x){
        ll sum = 1, ret;
        for(int i = 1; i <= S; i++){
            sum = sum * G % P;
            ret = search(sum * x % P);
            if(~ret && ret < P - 1) return ret - i;
        }
        assert(0);
        return -1;
    }
}
struct matrix {
    ll g[N][N];
    matrix(){
        memset(g, 0, sizeof(g));
    }
    matrix(int x){
        memset(g, 0, sizeof(g));
        for(int i = 1; i <= K; i++)
            g[i][i] = 1;
    }
    matrix operator * (const matrix &b){
        matrix c;
        for(int i = 1; i <= K; i++)
            for(int j = 1; j <= K; j++)
                for(int k = 1; k <= K; k++)
                    c.g[i][j] = (c.g[i][j] + g[i][k] * b.g[k][j]) % P2;
        return c;
    }
};
ll qpow(ll a, ll x){
    ll ret = 1;
    while(x){
        if(x & 1) ret = ret * a % P;
        a = a * a % P;
        x >>= 1;
    }
    return ret;
}
matrix qpow(matrix a, ll x){
    matrix ret(1);
    while(x){
        if(x & 1) ret = ret * a;
        a = a * a;
        x >>= 1;
    }
    return ret;
}
ll calcC(){
    matrix ret, op;
    ret.g[K][1] = 1;
    for(int i = 1; i < K; i++)
        op.g[i][i + 1] = 1;
    for(int i = 1; i <= K; i++)
        op.g[K][i] = b[K - i + 1];
    ret = qpow(op, n - K) * ret;
    return ret.g[K][1];
}
void exgcd(ll a, ll b, ll &g, ll &x, ll &y){
    if(!b) return (void)(x = 1, y = 0, g = a);
    exgcd(b, a % b, g, y, x);
    y -= x * (a / b);
}
ll solve(ll A, ll B){ //Ax % P2 == B, solve x
    ll a = A, b = P2, g, x, y;
    exgcd(a, b, g, x, y);
    if(B % g) return -1;
    x *= B / g, y *= B / g;
    ll t = b / g;
    x = (x % t + t) % t;
    return x;
}
int main(){
    BSGS::init();
    read(K);
    for(int i = 1; i <= K; i++) read(b[i]);
    read(n), read(m);
    C = calcC();
    m = BSGS::log(m);
    ll ans = solve(C, m);
    if(ans == -1) puts("-1");
    else write(qpow(G, ans)), enter;
    return 0;
}
Codeforces 1106F Lunar New Year and a Recursive Sequence | BSGS/exgcd/矩阵乘法的更多相关文章
- Codeforces 1106F Lunar New Year and a Recursive Sequence (数学、线性代数、线性递推、数论、BSGS、扩展欧几里得算法)
		哎呀大水题..我写了一个多小时..好没救啊.. 数论板子X合一? 注意: 本文中变量名称区分大小写. 题意: 给一个\(n\)阶递推序列\(f_k=\prod^{n}_{i=1} f_{k-i}b_i ... 
- @codeforces - 1106F@ Lunar New Year and a Recursive Sequence
		目录 @description@ @solution@ @accepted code@ @details@ @description@ 定义递推数列 f: (1)f[1] = f[2] = ... f ... 
- CF1106F Lunar New Year and a Recursive Sequence 原根、矩阵快速幂、BSGS
		传送门 好久没写数论题了写一次调了1h 首先发现递推式是一个乘方的形式,线性递推和矩阵快速幂似乎都做不了,那么是否能够把乘方运算变成加法运算和乘法运算呢? 使用原根!学过\(NTT\)的都知道\(99 ... 
- CF1106F Lunar New Year and a Recursive Sequence
		题目链接:CF1106F Lunar New Year and a Recursive Sequence 大意:已知\(f_1,f_2,\cdots,f_{k-1}\)和\(b_1,b_2,\cdot ... 
- HDU - 5950 Recursive sequence(二项式+矩阵合并+矩阵快速幂)
		Recursive sequence Farmer John likes to play mathematics games with his N cows. Recently, they are a ... 
- Codeforces 750E - New Year and Old Subsequence(线段树维护矩阵乘法,板子题)
		Codeforces 题目传送门 & 洛谷题目传送门 u1s1 我做这道 *2600 的动力是 wjz 出了道这个套路的题,而我连起码的思路都没有,wtcl/kk 首先考虑怎样对某个固定的串计 ... 
- CF1106F Lunar New Year and a Recursive Sequence 线性递推 + k次剩余
		已知\(f_i = \prod \limits_{j = 1}^k f_{i - j}^{b_j}\;mod\;998244353\),并且\(f_1, f_2, ..., f_{k - 1} = 1 ... 
- CF1106F Lunar New Year and a Recursive Sequence(矩阵快速幂+bsgs+exgcd)
		题面 传送门 前置芝士 \(BSGS\) 什么?你不会\(BSGS\)?百度啊 原根 对于素数\(p\)和自然数\(a\),如果满足\(a^x\equiv 1\pmod{p}\)的最小的\(x\)为\ ... 
- CF1106F Lunar New Year and a Recursive Sequence——矩阵快速幂&&bsgs
		题意 设 $$f_i = \left\{\begin{matrix}1 , \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ i < k\\ ... 
随机推荐
- JavaScript 格式化数字 - 转
			function number_format(number, decimals, dec_point, thousands_sep,roundtag) { /* * 参数说明: * number:要格 ... 
- JVM调优-GC参数
			一.Throughput收集器(吞吐量)-XX:+UseParallelGC-XX:+UseParallelOldGC *参数调整:通过调整堆大小,减少GC停顿时间,增大吞吐量增强堆大小可以减少Ful ... 
- Caffe源码中syncedmem文件分析
			Caffe源码(caffe version:09868ac , date: 2015.08.15)中有一些重要文件,这里介绍下syncedmem文件. 1. include文件: (1).& ... 
- ASP.NET Core使用log4net记录日志
			.NET常用的日志组件有NLog.Log4net等,.NET CORE下微软也自带了日志组件,到目前为止还没用过,而我本人常用的是log4net,下面简单讲讲.NET CORE下怎么使用log4net ... 
- Java中clone的写法
			Cloneable这个接口设计得十分奇葩,不符合正常人的使用习惯,然而用这个接口的人很多也很有必要,所以还是有必要了解一下这套扭曲的机制.以下内容来自于对Effective Java ed 2. it ... 
- 你应该学会的接口调试神器——Postman高级用法
			postman这个神器相信大家都用过,程序员作为非专业的测试人员,非常需要这么一款简单轻量级的restful测试工具,但是不知道你是否知道,postman的强大之处不只是测试一下接口,还有其他非常赞的 ... 
- Object-Oriented(一)创建对象
			自用备忘笔记 前言 虽然可以使用 Object 和对象字面量创建对象,但是如果要创建大量相似的对象又显得麻烦.为解决这个问题,人们开始使用工厂模式的变种. 工厂模式 function person(n ... 
- E. Binary Numbers AND Sum
			链接 [http://codeforces.com/contest/1066/problem/E] 题意 给你长度分别为n,m的二进制串,当b>0时,对a,b,&运算,然后b右移一位,把 ... 
- Scrum Meeting NO.8
			Scrum Meeting No.8 1.会议内容 2.任务清单 徐越 序号 近期的任务 进行中 已完成 1 代码重构:前端通讯模块改为HttpClient+Json √ 2 添加对cookies的支 ... 
- linux内核分析第二四学习报告
			学生 黎静 课程内容 计算机三大法宝 • 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: • 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来说并不那 ... 
