51nod 1544 变系数非波那契
原题链接https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1544
递推系数不是常数的一个递推
F(n)=F(n−1)S(n−1)+F(n−2)S(n−2)
有两种方法。
方法1:
根据递推关系。不断拆分。最终一定会得到一个类似于:
F(n)=F(0)A(n)+F(1)B(n)
S
是周期函数。(有bug的周期函数。。哈哈会扰动一下)
通过将F(0)=1,F(1)=0 和
F(0)=0,F(1)=1
计算不同的系数。
数组 A[],B[] 的贡献是可以累加的
并且有递推:
A[n]=S(n−1)A[n−1]+S(n−2)A[n−2]B[n]=S(n−1)B[n−1]+S(n−2)B[n−2]
整数周期矩阵快速幂。小周期利用 A[],B[] 计算
需要跳过周期中的某个数的时候。可以用倍增计算。
比如预处理:
A[k][i],表示F[i]对F[i+2k]贡献的系数
B[k][i],表示F[i+1]对F[i+2k]贡献的系数
令 F[i]=1,F[i+1]=0 带入 A[k−1][] 计算得:
F[i+2k−1]=A[k−1][i]F[i]+B[k−1][i]F[i+1]=A[k−1][i]
同理:
F[i+2k−1+1]=A[k−1][i+1]
所以:
A[k][i]=F[i+2k]F[i+2k]=A[k−1][i+2k−1]F[i+2k−1]+B[k−1][i+2k−1]F[i+2k−1+1]
同理 B[][] 也可以类似的 方法计算。
对于整数周期可以快速幂:
这种方法不是很好。因为编程不够方便。。倒不如直接全部使用矩阵维护。(因为中间还是需要矩阵)
方法2:
由
Fn=Fn−1Sn−1+Fn−2Sn−2
得:
[Sn−11Sn−20][Fn−1Fn−2]=[FnFn−1]
[Sn−11Sn−20]....[S21S10][S11S00][F1F0]=[FnFn−1]
S <script type="math/tex" id="MathJax-Element-20">S</script>存在周期 。使用线段树维护矩阵序列。配合快速幂。
下面是代码:
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define MAXN 50005
using namespace std;
typedef long long LL;
LL P;
LL s[MAXN];
struct mat
{
LL A[2][2];
mat()
{
memset(A,0,sizeof A);
}
mat(LL a,LL b)
{
A[1][0]=1;
A[1][1]=0;
A[0][0]=a;
A[0][1]=b;
}
void e()//单位化
{
A[0][0]=A[1][1]=1;
A[0][1]=A[1][0]=0;
}
void clear()
{
memset(A,0,sizeof A);
}
mat operator *(const mat &a)const
{
mat b;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
{
b.A[i][j]+=(A[i][k]*a.A[k][j])%P;
if(b.A[i][j]>=P)b.A[i][j]-=P;
}
return b;
}
};
struct point
{
LL j,v;
point()
{
j=0;
v=0;
}
bool operator <(const point a)const
{
return j<a.j;
}
}D[MAXN];
struct node
{
int c[2];
mat key;
node()
{
c[0]=c[1]=0;
key.e();
}
};
struct Bt
{
node A[MAXN*2+1000];
int deep;
int n;
int root;
mat ans;
Bt()
{
deep=2;
root=1;
}
void _insert(int x,int L,int R,const mat &key,int &k)
{
if(x<L||x>R)return ;
if(!k)k=deep++;
if(L==R)
{
A[k].key=key;
return;
}
int mid=(L+R)>>1;
_insert(x,L,mid,key,A[k].c[0]);
_insert(x,mid+1,R,key,A[k].c[1]);
A[k].key=A[A[k].c[1]].key*A[A[k].c[0]].key;
}
void insert(int x,const mat &key)
{
_insert(x,0,n,key,root);
}
void _query(int l,int r,int L,int R,int k)
{
if(R<l||L>r)return ;
if(l<=L&&R<=r)
{
ans=A[k].key*ans;
return;
}
int mid=(L+R)>>1;
_query(l,r,L,mid,A[k].c[0]);
_query(l,r,mid+1,R,A[k].c[1]);
}
mat query(int l,int r)
{
ans.e();
_query(l,r,0,n,root);
return ans;
}
}T;
int n,m;
mat RE;
mat Pow(mat a,LL b)
{
mat tmp;
tmp.e();
while(b)
{
if(b&1)
tmp=tmp*a;
a=a*a;
b>>=1;
}
return tmp;
}
mat slove(LL L,LL R)
{
mat ans;
ans.e();
if(R<L) return ans;
if(L/n==R/n) return T.query(L%n,R%n);
LL u=L+n-L%n;
LL d=R-R%n;
ans=Pow(RE,(d-u)/n);
ans=ans*T.query(L%n,n-1);
ans=T.query(0,R%n)*ans;
return ans;
}
int main ()
{
LL k;
scanf("%lld %lld",&k,&P);
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%lld",s+i);
scanf("%d",&m);
for(int i=0;i<m;i++) scanf("%lld %lld",&D[i].j,&D[i].v);
if(k==0)
{
printf("0\n");
return 0;
}
sort(D,D+m);
T.n=n-1;
for(int i=1;i<n;i++) T.insert(i,mat(s[i],s[i-1]));
T.insert(0,mat(s[0],s[n-1]));
RE=T.query(0,n-1);
LL L=1;
mat ans;
ans.e();
for(int i=0;i<m&&L<k;i++)
{
LL lim=min(k-1,D[i].j-1);
ans=slove(L,lim)*ans;
L=lim+1;
if(L==k)break;
int t=i+1;
while(D[t].j==D[t-1].j+1&&t<m)t++;
ans= mat(D[i].v,s[(D[i].j-1+n)%n]) * ans;
L++;
for(i++;i<t&&L<k;i++,L++) ans=mat(D[i].v,D[i-1].v)*ans;
if(L==k)break;
ans= mat(s[L%n],D[i-1].v) * ans;
L++;
if(L==k)break;
i--;
}
ans=slove(L,k-1)*ans;
printf("%lld\n",ans.A[0][0]);
return 0;
}