[CF1106F]Lunar New Year and a Recursive Sequence

2024-03-16 23:32

本文主要是介绍[CF1106F]Lunar New Year and a Recursive Sequence,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Lunar New Year and a Recursive Sequence

题解

 忽的发现这题难度只有2400,还是我数学太菜了

如果像原式那样对整个式子每次都进行次方的更新的话,明显会T掉,我们可以对它的幂进行更新,因为做出贡献的项只有f_{k}这一个,每个f_{i}都可以只用f_{k}表示出来。

f_{i}f_{k}p_{i}次方,可得p_{i}=\sum_{j=1}^{k}b_{j}\cdot p_{i-j},可如果暴力转移的话10^9级别的n明显会让我们T掉。

发现这个式子很容易用矩阵乘法实现转换,矩阵乘法就可以被定义为

A=\begin{bmatrix} 0 &0 & \cdots & 0 &b_{k} \\ 1 & 0 &\cdots &0 & b_{k-1} \\ 0 & 1 & \cdots &0 &b_{k-2} \\ \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 &\cdots & 1 & b_{1} \end{bmatrix} , S= \begin{bmatrix} 0 & 0 & \cdots & 0 & 1 \end{bmatrix}

AS两矩阵相乘来进行转换。

这样来进行转换。这样,第n项的指数就是S \times A^{n-1}时的第1项了。

因为这是求的次数,所以必须模998244353

由于a^{x}\equiv b (mod \: 998244353)。由于998244353的原根为3,所以肯定有a\equiv 3^c (mod \: 998244353)

原式也可以被表示为3^{cx} \equiv b (mod \: 998244353)

接下来我们可以用BSGS求出cx\: mod\: 998244352的值,用乘法逆元将这个x消去,就可以得到c的取值。答案即为3^{c}

于是就可以用O\left(k^{3}log_{n} \right )的时间复杂度求出答案了。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
typedef long long LL;
#define int LL
#define MAXN 500005
const double eps=1e-7;
const int INF=0x7f7f7f7f; 
const LL mo=998244353;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){_T f=1;x=0;char s=getchar();while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}x*=f;
}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;
}
int add(int x,int y){return x+y>=mo-1?x+y-mo+1:x+y;}
int K;
struct matrix{int c[105][105];matrix(){memset(c,0,sizeof(c));}friend matrix operator * (const matrix &a,const matrix &b){matrix res;for(int i=1;i<=K;i++)for(int k=1;k<=K;k++)if(a.c[i][k])for(int j=1;j<=K;j++) res.c[i][j]=add(res.c[i][j],1ll*a.c[i][k]*b.c[k][j]%(mo-1));return res;}
}A,B,C;
matrix mat_qkpow(matrix a,int s){matrix t;for(int i=1;i<=K;i++)t.c[i][i]=1;while(s){if(s&1)t=a*t;a=a*a;s>>=1;} return t;
}
map<int,int>mp;
int bsgs(LL p,int b,int n){int tmp=1,sum=1,m=ceil(sqrt(p));mp[n%p]=0;for(int i=1;i<=m;i++)tmp=1ll*tmp*b%p,mp[1ll*tmp*n%p]=i;for(int j=1;j<=m;j++){sum=1ll*sum*tmp%p;if(mp[sum])return j*m-mp[sum];}return -1;
}
int exgcd(int a,int b,int &x,int &y){if(!b){x=1;y=0;return a;}int d=exgcd(b,a%b,y,x);y-=x*(a/b);return d;
}
signed main(){read(K);A.c[1][K]=1;for(int i=K;i>0;i--)read(B.c[i][K]);for(int i=2;i<=K;i++)B.c[i][i-1]=1;int n,m;read(n);read(m);C=A*mat_qkpow(B,n-1);int t=bsgs(mo,3,m);if(t==-1){puts("-1");return 0;}int x,y,d=exgcd(C.c[1][1],mo-1,x,y);if(t%d){puts("-1");return 0;}x=(t/d*x%(mo-1)+mo-1)%(mo-1);printf("%lld\n",qkpow(3,x));return 0;
}

谢谢!!!

---updata by 2020.10.5---

再做一遍感觉那个乘法逆元好难调,注意不能因为两者互质就直接没有乘法逆元。

这篇关于[CF1106F]Lunar New Year and a Recursive Sequence的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/817090

相关文章

java线程深度解析(一)——java new 接口?匿名内部类给你答案

http://blog.csdn.net/daybreak1209/article/details/51305477 一、内部类 1、内部类初识 一般,一个类里主要包含类的方法和属性,但在Java中还提出在类中继续定义类(内部类)的概念。 内部类的定义:类的内部定义类 先来看一个实例 [html]  view plain copy pu

string字符会调用new分配堆内存吗

gcc的string默认大小是32个字节,字符串小于等于15直接保存在栈上,超过之后才会使用new分配。

List list = new ArrayList();和ArrayList list=new ArrayList();的区别?

List是一个接口,而ArrayList 是一个类。 ArrayList 继承并实现了List。 List list = new ArrayList();这句创建了一个ArrayList的对象后把上溯到了List。此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。而ArrayList list=new ArrayList();创建一对象则保留了A

浙大数据结构:02-线性结构4 Pop Sequence

这道题我们采用数组来模拟堆栈和队列。 简单说一下大致思路,我们用栈来存1234.....,队列来存输入的一组数据,栈与队列进行匹配,相同就pop 机翻 1、条件准备 stk是栈,que是队列。 tt指向的是栈中下标,front指向队头,rear指向队尾。 初始化栈顶为0,队头为0,队尾为-1 #include<iostream>using namespace std;#defi

【UVA】1626-Brackets sequence(动态规划)

一道算是比较难理解的动规。 状态转移分2个: (用d[i][j]表示在i~j内最少需要添加几个括号,保持平衡) 1.如果s[i]和s[j]是一对括号,那么d[i][j] = d[i + 1][j - 1] 2.否则的话 d[i][j] = min(d[i][k],[k + 1][j]); 边界是d[i + 1][i] = 0; d[i][i] = 1; 13993644 162

【UVA】10534 - Wavio Sequence(LIS最长上升子序列)

这题一看10000的数据量就知道必须用nlog(n)的时间复杂度。 所以特意去看了最长上升子序列的nlog(n)的算法。 如果有2个位置,该位置上的元素为A[i]和A[j],并且他们满足以下条件: 1.dp[i] = dp[j]    (dp[x]代表以x结尾的最长上升子序列长度) 2.A[i] < A[j] 3.i < j 那么毫无疑问,选择dp[i] 一定优于选择dp[j] 那么

vue原理分析(六)--研究new Vue()

今天我们来分析使用new Vue() 之前研究时,只是说是在创建一个实例。并没有深入进行研究 在vue的源码中找下Vue的构造函数 function Vue(options) {if (!(this instanceof Vue)) {warn$2('Vue is a constructor and should be called with the `new` keyword');}thi

2015年多校联合训练第一场OO’s Sequence(hdu5288)

题意:给定一个长度为n的序列,规定f(l,r)是对于l,r范围内的某个数字a[i],都不能找到一个对应的j使得a[i]%a[j]=0,那么l,r内有多少个i,f(l,r)就是几。问所有f(l,r)的总和是多少。 公式中给出的区间,也就是所有存在的区间。 思路:直接枚举每一个数字,对于这个数字,如果这个数字是合法的i,那么向左能扩展的最大长度是多少,向右能扩展的最大长度是多少,那么i为合法的情况

GTK中创建线程函数g_thread_new和g_thread_create的区别

使用GThread函数,需要引用glib.h头文件。 这两个接口的核心区别就是  g_thread_create 是旧的接口,现在已经不使用了,而g_thread_new是新的接口,建议使用。 g_thread_create: g_thread_create has been deprecated since version 2.32 and should not be used in n

New的VC编译器实现

当我们调用 new 的时候,例如 int *p = new int; 时,编译器到底作了什么工作呢?跟进断点看一看。   (在 vc debug模式下 ) double *p1 = new double ; 00411A6E  push        8    00411A70  call        operator new (4111B8h) 00411A75  add