51nod 1544 变系数非波那契

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)=0 F(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 <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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值