51nod 1544 变系数非波那契

来源:互联网 发布:120首网络音乐产品 编辑:程序博客网 时间:2024/05/18 09:05

51nod 1544 变系数非波那契

原题链接https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1544

递推系数不是常数的一个递推

F(n)=F(n1)S(n1)+F(n2)S(n2)

有两种方法。

方法1:

根据递推关系。不断拆分。最终一定会得到一个类似于:

F(n)=F(0)A(n)+F(1)B(n)

S是周期函数。(有bug的周期函数。。哈哈会扰动一下)

通过将F(0)=1,F(1)=0F(0)=0,F(1)=1计算不同的系数。

数组A[],B[]的贡献是可以累加的

并且有递推:

A[n]=S(n1)A[n1]+S(n2)A[n2]B[n]=S(n1)B[n1]+S(n2)B[n2]

整数周期矩阵快速幂。小周期利用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[k1][]计算得:

F[i+2k1]=A[k1][i]F[i]+B[k1][i]F[i+1]=A[k1][i]

同理:

F[i+2k1+1]=A[k1][i+1]

所以:

A[k][i]=F[i+2k]F[i+2k]=A[k1][i+2k1]F[i+2k1]+B[k1][i+2k1]F[i+2k1+1]

同理B[][]也可以类似的 方法计算。

对于整数周期可以快速幂:

这种方法不是很好。因为编程不够方便。。倒不如直接全部使用矩阵维护。(因为中间还是需要矩阵)

方法2:

Fn=Fn1Sn1+Fn2Sn2

得:
[Sn11Sn20][Fn1Fn2]=[FnFn1]

[Sn11Sn20]....[S21S10][S11S00][F1F0]=[FnFn1]

S存在周期 。使用线段树维护矩阵序列。配合快速幂。

下面是代码:

#include <stdio.h>#include <algorithm>#include <string.h>#define MAXN 50005using 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;}
原创粉丝点击