大家好,又见面了,我是你们的朋友全栈君。
题目大意:给定序列迭代规则,求一段的序列和。特点是要求的序列很长。
Solution#
观察到,由于是求和,我们可以想到前缀和的思想。也就是说,对于求\(\sum_{i=m}^n a_i\),我们只需要求\(\sum_{i=1}^{m-1}a_i\)和\(\sum_{i=1}^n a_i\),然后做差即可。
注意到\(n,m\)的范围,我们线性递推是不现实的。于是我们可以考虑矩阵快速幂。
考虑维护\(k+1\)个值。维护\(k\)个序列基本值,以及一个求和值。我们根据\(k\)个信息推出下一个信息。观察到\(k\)的范围,所以是可以做的。
构造矩阵比较简单。注意一下\(c\)的位置,是倒序的。
由于我们一次维护的是\(k\)个值,所以,如果我们要求\(pos\),则我们求出转移矩阵的\(pos-k\)次方就可以求出。
两次矩阵快速幂加上前缀和思想,这题做完了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
ll n,m,p,s,ss[16],Ans,sss;
inline ll add(ll x,ll y){return (x%p+y%p)%p;}
inline ll mul(ll x,ll y){return (x%p*y%p)%p;}
inline ll del(ll x,ll y){return x-y<0?x+p-y:x-y;}
int k,b[16],c[16];
struct Mat{
ll A[17][17];
Mat(){memset(A,0,sizeof(A));}
Mat operator*(const Mat&B)const{
Mat res;
for(int i=1;i<=k+1;++i)
for(int j=1;j<=k+1;++j)
for(int l=1;l<=k+1;++l)
res.A[i][j]=add(res.A[i][j],mul(A[i][l],B.A[l][j]));
return res;
}
}w,s1,A2,A1;
inline void Init(Mat &x){for(int i=1;i<=k+1;++i)x.A[i][i]=1;}
Mat qpow(Mat st,ll b){
Mat c;
Init(c);
while(b){
if(b&1)c=c*st;
b>>=1;st=st*st;
}
return c*s1;
}
signed main(){
scanf("%lld",&k);
for(int i=1;i<=k;++i)scanf("%lld",&b[i]);
for(int i=1;i<=k;++i)scanf("%lld",&c[i]);
scanf("%lld%lld%lld",&m,&n,&p);
for(int i=1;i<=k;++i)ss[i]=add(ss[i-1],(ll)b[i]);
for(int i=1;i<=k;++i)s1.A[i][1]=b[i];
s1.A[k+1][1]=ss[k];
for(int i=1;i<k;++i)w.A[i][i+1]=1;
for(int i=1;i<=k;++i)w.A[k][i]=c[k-i+1],w.A[k+1][i]=c[k-i+1];
w.A[k+1][k+1]=1;
if(m-1>=k)A1=qpow(w,m-1-k);
else A1.A[k+1][1]=ss[m-1];
if(n>=k)A2=qpow(w,n-k);
else A2.A[k+1][1]=ss[n];
Ans=del(A2.A[k+1][1],A1.A[k+1][1]);
printf("%lld\n",Ans);
return 0;
}
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/155600.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...