CF 1106 F. Lunar New Year and a Recursive Sequence

86 篇文章 0 订阅
37 篇文章 1 订阅

给出一个k阶递推式 ( m o d 998244353 ) \pmod{998244353} (mod998244353) f [ 1... k − 1 ] = 1 , f [ n ] = m f[1...k-1] = 1,f[n] =m f[1...k1]=1,f[n]=m
f [ k ] f[k] f[k]
这个递推式显然让人想两边同时求一个对数。
g [ i ] = log ⁡ f [ i ] g[i] = \log f[i] g[i]=logf[i]
g [ i ] = ∑ j = 1 k b [ j ] g [ i − j ] g[i] = \sum_{j=1}^k b[j]g[i-j] g[i]=j=1kb[j]g[ij]
常系数线性递推???
然后发现 g [ 1... k − 1 ] = 0 g[1...k-1] = 0 g[1...k1]=0
g [ n ] g[n] g[n]又是 g [ 1... k ] g[1...k] g[1...k]的线性组合。
那么设 g [ n ] = ∑ i = 1 k c [ i ] ∗ g [ i ] g[n] = \sum_{i=1}^kc[i] * g[i] g[n]=i=1kc[i]g[i]
可以发现 g [ n ] = c [ k ] ∗ g [ k ] g[n] = c[k] * g[k] g[n]=c[k]g[k]
c[k]可以通过特征多项式+多项式取模来计算矩阵快速幂来计算。
g[n]可以通过。。。好吧这里 log ⁡ \log log的底数可以任意所以模意义下的 log ⁡ \log log我们可以用998244353的原根3做底数,然后模意义下的 log ⁡ \log log就可以用bsgs来 O ( n ) O(\sqrt n) O(n )计算了。
然后就是同余方程:
c [ k ] ∗ g [ k ] = g [ n ] ( m o d 998244353 ) c[k] * g[k] = g[n] \pmod {998244353} c[k]g[k]=g[n](mod998244353)
这个gcd一下就可以判断有无解。
exgcd一下就可以计算出一组解。
这个题好像就这么精彩的结束了?(好像可以把k开到 1 0 5 10^5 105的样子)
AC Code:

#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#define maxn 105
#define mod 998244352
#define Mod 998244353
#define S 100005
using namespace std;

map<int,int>mp;
int k,n,m;

struct mat
{
	int a[maxn][maxn];
	mat(){memset(a,0,sizeof a);}
	mat operator *(const mat &B)const
	{
		mat ret;
		for(int i=0;i<k;i++)
			for(int j=0;j<k;j++) if(a[i][j])
				for(int p=0;p<k;p++) if(B.a[j][p])
					ret.a[i][p] = (ret.a[i][p] + 1ll * a[i][j] * B.a[j][p]) % mod;
		return ret;
	}
}b;

mat Pow(mat base,int p)
{
	mat ret;
	for(int i=0;i<k;i++)
		ret.a[i][i] = 1;
	for(;p;p>>=1,base=base*base) 
		if(p&1) 
			ret = ret * base;
	return ret;
}

int Pow(int base,int p)
{
	int ret = 1;
	for(;p;p>>=1,base=1ll*base*base%998244353)
		if(p&1)
			ret = ret * 1ll * base % 998244353;
	return ret;
}

void exgcd(int a,int b,int &x,int &y,int &gcd)
{
	if(!b) gcd=a,x=1,y=0;
	else exgcd(b,a%b,y,x,gcd),
		y-=a/b*x;
}

int main()
{
	scanf("%d",&k);
	for(int i=0;i<k;i++) scanf("%d",&b.a[0][i]);
	for(int i=1;i<k;i++) b.a[i][i-1] = 1;
	scanf("%d%d",&n,&m);
	n-=k;
	b = Pow(b,n);
	int tmp = b.a[0][0];
	
	
	int G = 3;
	for(int i=0,s=1;i<S;i++,s=1ll*s*G%998244353) 
		mp[s] = i;
	int Gp = Pow(Pow(G,S),Mod-2);
	int zb = 0;
	for(int i=0,s=m,tmp;i<S;i++,s=1ll*s*Gp%998244353)
		if(mp.count(s))
		{
			zb = mp[s] + i * S;
			break;
		}
	
	int x,y,gcd;
	exgcd(tmp,998244352,x,y,gcd);	
	if(zb % gcd) puts("-1");
	else
	{
		zb /= gcd;
		x = 1ll * x * zb % mod;
		printf("%d\n",Pow(3,(x+mod)%mod));
	}	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值