【CodeChef RNG】Random Number Generator(多项式取模优化常系数线性递推)

题目大意

给定数组 A A A的前 K K K位,和一个 K K K位的数组 C C C 1 ≤ K ≤ 30000 1\leq K\leq 30000 1K30000),
A i = ∑ j = 1 K A i − j × C j A_i=\sum_{j=1}^K A_{i-j}\times C_j Ai=j=1KAij×Cj
A N A_N AN 1 ≤ N ≤ 1 0 18 1\leq N\leq 10^{18} 1N1018)。

subtask 1: 1 ≤ K ≤ 3000 1\leq K\leq3000 1K3000
subtask 2: 1 ≤ K ≤ 30000 1\leq K\leq 30000 1K30000

题解

本来要用 Cayley-Hamilton定理 ,奇怪的玩意儿,但我看不懂X﹏X。。。于是尝试避开这个东西来理解。

最后的结果一定是从A的前k位转移过来的,设结果:
a n s = f 1 A 1 + f 2 A 2 + f 3 A 3 + . . . + f K A K ans=f_1A_1+f_2A_2+f_3A_3+...+f_KA_K ans=f1A1+f2A2+f3A3+...+fKAK
N = 1 N=1 N=1时:
f : 1 0 0 . . . 0 f:\begin{matrix} 1&0&0&...&0\\ \end{matrix} f:100...0
N = 2 N=2 N=2时:
f : 0 1 0 . . . 0 f:\begin{matrix} 0&1&0&...&0\\ \end{matrix} f:010...0
相当于每次都右移1位。
直到 N = K + 1 N=K+1 N=K+1
f : C K C K − 1 C K − 2 . . . C 1 f:\begin{matrix} C_K&C_{K-1}&C_{K-2}&...&C_1\\ \end{matrix} f:CKCK1CK2...C1
如果把 f f f看作多项式:
f ( x ) = f 1 + f 2 x + f 3 x 2 + . . . + f K x K − 1 f(x)=f_1+f_2x+f_3x^2+...+f_Kx^{K-1} f(x)=f1+f2x+f3x2+...+fKxK1
右移操作就相当于将 f ( x ) f(x) f(x)变为 x f ( x ) xf(x) xf(x)
如果出现了第 x K x^K xK项,需要把它化为 x 0 x^0 x0~ x K − 1 x^{K-1} xK1
x K = C K + C K − 1 x + C K − 2 x 2 + . . . + C 1 x K − 1 x^K=C_K+C_{K-1}x+C_{K-2}x^2+...+C_1x^{K-1} xK=CK+CK1x+CK2x2+...+C1xK1
p ( x ) = x K − C K − C K − 1 x − C K − 2 x 2 − . . . − C 1 x K − 1 p(x)=x^K-C_K-C_{K-1}x-C_{K-2}x^2-...-C_1x^{K-1} p(x)=xKCKCK1xCK2x2...C1xK1
实际上就是把多项式 f ( x )   m o d   p ( x ) f(x)\space mod\space p(x) f(x) mod p(x),就可以把 x K x_K xK及以后的项全部化为 x 0 x^0 x0~ x K − 1 x^{K-1} xK1
因为:
f K + 1 x K f_{K+1}x^K fK+1xK取模相当于把 f ( x ) f(x) f(x)变为 f ( x ) − f K + 1 x K + f K + 1 C K + f K + 1 C K − 1 x + . . . + f K + 1 C 1 x K − 1 f(x)-f_{K+1}x^K+f_{K+1}C_K+f_{K+1}C_{K-1}x+...+f_{K+1}C_1x^{K-1} f(x)fK+1xK+fK+1CK+fK+1CK1x+...+fK+1C1xK1

令转移多项式 t ( x ) = x t(x)=x t(x)=x
最终结果即为 f = t N   m o d   p f=t^N\space mod\space p f=tN mod p ;
利用多项式快速幂,多项式取模可以做到 O ( K ( log ⁡ 2 K ) ( log ⁡ 2 N ) ) O(K(\log_2 K) (\log_2 N)) O(K(log2K)(log2N))

推广

凡是第 i i i项可以由前面 k k k项通过常数系数得到的递推(实际上就是可以使用矩阵加速的递推),都可以转为多项式取模,若 A i = ∑ j = 1 k f j A i − j A_i=\sum_{j=1}^kf_jA_{i-j} Ai=j=1kfjAij
则可以通过取模 p ( x ) = x k − ∑ j = 1 k f j x k − j p(x)=x^k-\sum_{j=1}^kf_jx^{k-j} p(x)=xkj=1kfjxkj
进行递推。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXK=30005,MOD=104857601,ROOT=3;
typedef int Poly[MAXK*5];

int PowMod(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=(1LL*res*a)%MOD;
		a=(1LL*a*a)%MOD;
		b>>=1;
	}
	return res;
}

void NTT(Poly &a,int n,int mode)
{
	for(int i=0,j=0;i<n;i++)
	{
		if(i<j)
			swap(a[i],a[j]);
		int k=n>>1;
		while(k&&(k&j))
			j^=k,k>>=1;
		j^=k;
	}
	for(int i=1;i<n;i<<=1)
	{
		int w1=PowMod(ROOT,(MOD-1)/(i<<1)),w=1;
		if(mode==-1)
			w1=PowMod(w1,MOD-2);
		for(int j=0;j<i;j++,w=(1LL*w*w1)%MOD)
			for(int l=j,r=l+i;l<n;l+=(i<<1),r=l+i)
			{
				int temp=(1LL*a[r]*w)%MOD;
				a[r]=(a[l]-temp+MOD)%MOD;
				a[l]=(a[l]+temp)%MOD;
			}
	}
	if(mode==-1)
	{
		int inv=PowMod(n,MOD-2);
		for(int i=0;i<n;i++)
			a[i]=(1LL*a[i]*inv)%MOD;
	}
}
void Inv(Poly &a,Poly &i,int n)
{
	static Poly temp;
	if(n==1)
	{
		i[0]=PowMod(a[0],MOD-2);
		return;
	}
	Inv(a,i,(n+1)/2);
	int len=1;
	while(len<n*2-1)
		len<<=1;
	copy(a,a+n,temp);
	fill(temp+n,temp+len,0);
	NTT(temp,len,1);
	fill(i+n,i+len,0);
	NTT(i,len,1);
	for(int j=0;j<len;j++)
		i[j]=1LL*i[j]*((2LL-1LL*temp[j]*i[j]%MOD+MOD)%MOD)%MOD;
	NTT(i,len,-1);
	fill(i+n,i+len,0);
}
void Mod(Poly &a,const Poly &p,int n,int m)
{
	static Poly temp,p0,p1,c;
	copy(a,a+n,temp);
	reverse(temp,temp+n);
	copy(p,p+m,p0);
	reverse(p0,p0+m);
	Inv(p0,p1,n-m+1);
	int len=1;
	while(len<(n-m+1)*2-1)
		len<<=1;
	fill(temp+n-m+1,temp+len,0);
	NTT(temp,len,1);
	fill(p1+n-m+1,p1+len,0);
	NTT(p1,len,1);
	for(int i=0;i<len;i++)
		c[i]=(1LL*temp[i]*p1[i])%MOD;
	NTT(c,len,-1);
	fill(c+n-m+1,c+len,0);
	reverse(c,c+n-m+1);
	len=1;
	while(len<n)
		len<<=1;
	NTT(c,len,1);
	fill(p0+m,p0+len,0);
	reverse(p0,p0+m);
	NTT(p0,len,1);
	for(int i=0;i<len;i++)
		c[i]=(1LL*c[i]*p0[i])%MOD;
	NTT(c,len,-1);
	for(int i=0;i<n;i++)
		a[i]=(a[i]-c[i]+MOD)%MOD;
}
void MulMod(Poly &a,const Poly &b,const Poly &p,int n)
{
	static Poly temp;
	copy(b,b+n,temp);
	int len=1;
	while(len<n*2)
		len<<=1;
	fill(a+n,a+len,0);
	fill(temp+n,temp+len,0);
	NTT(a,len,1);
	NTT(temp,len,1);
	for(int i=0;i<len;i++)
		a[i]=(1LL*a[i]*temp[i])%MOD;
	NTT(a,len,-1);
	Mod(a,p,n*2-1,n);
}
void PowMod(Poly &res,Poly &a,long long b,const Poly &p,int n)
{
	fill(res,res+n,0);
	res[0]=1;
	while(b)
	{
		if(b&1LL)
			MulMod(res,a,p,n);
		MulMod(a,a,p,n);
		b>>=1LL;
	}
}

Poly A,C,p,t,f;
int main()
{
	long long N;
	int K;
	scanf("%d%lld",&K,&N);
	for(int i=0;i<K;i++)
		scanf("%d",&A[i]);
	for(int i=0;i<K;i++)
		scanf("%d",&C[i]);
	
	p[K]=1;
	for(int i=0;i<K;i++)
		p[i]=(MOD-C[K-i-1])%MOD;
	t[1]=1;
	PowMod(f,t,N-1,p,K+1);
	
	int ans=0;
	for(int i=0;i<K;i++)
		ans=(ans+(1LL*A[i]*f[i])%MOD)%MOD;
	printf("%d\n",ans);
	
	return 0;
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值