题解 P2461 【[SDOI2008]递归数列】

Shikita

2019-03-14 18:58:09

Solution

# 矩阵乘法 线性递推式,求几项之和 按照惯例,我们先手推一波~~虽然并推不出来~~ 设当前项为 $a_i$ 当$i\leqslant k$ 时 $$a_i=b_i$$ 当$i>k$ 时 $$a_i= \sum_{j=1}^{k} c_j*a_{i-j}$$ 同理 $$a_{i+1}= \sum_{j=1}^{k} c_j * a_{i-j+1}$$ 我们尝试着减一下 $$a_{i+1}-a_i= \sum_{j=1}^{k} c_j * ( a_{i-j+1}-a_{i-j} )$$ 然后我们逐渐往前推,便可以得到一个性质 $a_i-a_{i-1}=$ 之前所有的差乘上一个$c_j$ ~~这不是废话吗,题意显然啊~~ 接下来我们便可以构造出这么一个矩阵 ~~那你直接给矩阵不好吗~~ $sum[i]$ 表示的是$b_i$的前缀和,我们先假设$\quad k=3$ $( a_{n-2} \quad a_{n-1} \quad a_n \quad s_n )$=$(a_1\quad a_2\quad a_3 \quad s_3)$ $\begin{bmatrix}0&0&c_3&c_3\\1&0&c_2&c_2\\0&1&c_1&c_1\\0&0&0&1\\\end{bmatrix}^{n-3}$ 举完例子,我们再详细说一下矩阵($k*k$)具体构造 对于第$i$行($1<=i<=k$),我们从第$k$个开始(前面全部用0填充),之后全部用 $c_{k-i+1}$ 进行填充 然后我们对于第$i$行($1<=i<=k$)再进行一次对角线填充 最后我们再多加上一行,在最后一个填充一个1,完成对角线填充 然后我们就可以愉快的开始快速幂处理了 ``` #include<bits/stdc++.h> #define ll long long using namespace std; inline ll read() { ll x=0; char c=getchar(); bool flag=0; while(c<'0'||c>'9') {if(c=='-')flag=1;c=getchar();} while(c>='0'&&c<='9'){x=(x+(x<<2)<<1)+ c-'0';c=getchar();} return flag?-x:x; } const int N=20; ll K,b[N],c[N],sum[N],ans1,ans2,mod,n,m; struct Matrix { int l,r; ll a[N][N]; Matrix(){}; Matrix(int x,int y):l(x),r(y){ memset(a,0,sizeof(a));} Matrix operator*(Matrix t) { Matrix cg=Matrix(l,t.r); for(int i=1;i<=l;++i) for(int j=1;j<=t.r;++j) for(int k=1;k<=r;++k) cg.a[i][j]=(cg.a[i][j]+a[i][k]*t.a[k][j]%mod)%mod; return cg; } Matrix operator^(ll p) { Matrix t=Matrix(l,r),cg=Matrix(l,r); memcpy(t.a,a,sizeof(a)); if(p&1) memcpy(cg.a,a,sizeof(a)); else for(int i=1;i<=l;++i) cg.a[i][i]=1; while(p>>=1) { t=t*t; if(p&1) cg=cg*t; } return cg; } }st,ed; int main() { K=read(); st=Matrix(1,K+1); for(int i=1;i<=K;++i) b[i]=read(); for(int i=1;i<=K;++i) c[i]=read(); m=read(),n=read(),mod=read(); for(int i=1;i<=K;++i) sum[i]=(sum[i-1]+b[i])%mod,st.a[1][i]=b[i]%mod,c[i]%=mod; if(n<=K) {printf("%lld\n",(sum[n]-sum[m-1]+mod)%mod);return 0;} ed=Matrix(K+1,K+1); for(int i=1;i<=K;++i) ed.a[i][K]=ed.a[i][K+1]=c[K-i+1]; for(int i=2;i<=K;++i) ed.a[i][i-1]=1; ed.a[K+1][K+1]=1,st.a[1][K+1]=sum[K]; if(m>K) ans1=(st*(ed^(m-K-1))).a[1][K+1]; else ans1=sum[m-1]; ans2=(st*(ed^(n-K))).a[1][K+1]; printf("%lld\n",(ans2-ans1+mod)%mod); } ```