华南理工大学“三七互娱杯” B HRY and fibonacci //线段树+矩阵快速幂(矩阵化lazy)

 华南理工大学“三七互娱杯” B HRY and fibonacci  //线段树+矩阵快速幂(矩阵化lazy)

题意:fib是斐波那契数列,fic是fib的前缀和,fid是fic的前缀和,给出q个询问,

 题解:

得到了fid的递推式子,需要快速求出fid,并且需要得到区间的和,矩阵快速幂+线段树。

线段树维护一段区间 fid(ai)的和,fid(ai-1)的和,还有ai的和,1的和不需要维护(r-l+1)

lazy是矩阵状的,维护的是乘的矩阵。pushDown时,让矩阵相乘。

为什么可以求和再乘,因为满足乘法分配律,和的乘积==乘积的和,然后就可以利用线段树维护。

运行时间:4000/5000ms,有点卡过的感觉,看到有维护2*2矩阵的,不知道怎么搞..

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 100000007;
const int max_n = 1e5+6;

struct mat{LL m[4][4];};
mat xx;//乘的矩阵
mat zzz;//单位矩阵
int a[max_n];
LL d[max_n<<2],d_1[max_n<<2],sa[max_n<<2];//∑fid(ai),∑fid(ai-1),∑ai
mat lazy[max_n<<2];//矩阵状的lazy

mat mat_mul(mat aa,mat bb,int nn){ 
    mat re;memset(re.m,0,sizeof(re.m));
    for(int i=0;i<nn;i++){
        for(int j=0;j<nn;j++){
            for(int k=0;k<nn;k++){
                re.m[i][j]=(re.m[i][j]+aa.m[i][k]*bb.m[k][j])%mod;
            }
        }
    }
    return re;
}
mat mat_quick_mod(mat jin,LL x,int nn){
    mat re;memset(re.m,0,sizeof(re.m));
    for(int i=0;i<nn;i++) re.m[i][i]=1;
    while(x){
        if(x&1) re=mat_mul(re,jin,nn);
        x=x>>1;jin=mat_mul(jin,jin,nn);
    }
    return re;
}
bool mat_same(mat aa,mat bb,int n){
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            if(aa.m[i][j]!=bb.m[i][j])return 0;
    return 1;
}
void init_mat(mat &aa,int nn){//初始化为单位矩阵
    memset(aa.m,0,sizeof(aa.m));
    for(int i=0;i<nn;i++) aa.m[i][i]=1;
}
void jin_mat_init(){//乘的转移矩阵
    memset(xx.m,0,sizeof(xx.m));
    xx.m[0][0]=1;xx.m[0][1]=1;xx.m[0][2]=0;xx.m[0][3]=0;
    xx.m[1][0]=1;xx.m[1][1]=0;xx.m[1][2]=0;xx.m[1][3]=0;
    xx.m[2][0]=1;xx.m[2][1]=0;xx.m[2][2]=1;xx.m[2][3]=0;
    xx.m[3][0]=1;xx.m[3][1]=0;xx.m[3][2]=1;xx.m[3][3]=1;
}

LL fid(LL ii,int nn){//fid第n项
    if(ii==0)return 0;if(ii==1)return 1;if(ii==2)return 3;
    mat re=mat_quick_mod(xx,ii-2,nn);
    return (re.m[0][0]*3+re.m[1][0]*1+re.m[2][0]*2+re.m[3][0])%mod;
}


void pushUp(int root){
    d[root]=(d[root<<1]+d[root<<1|1])%mod;
    d_1[root]=(d_1[root<<1]+d_1[root<<1|1])%mod;
    sa[root]=(sa[root<<1]+sa[root<<1|1])%mod;
}
void upup(int root,int le,mat now){//更新区间时乘lazy矩阵
    LL aa[4]={d[root],d_1[root],sa[root],le};
    d[root]=0;d_1[root]=0;sa[root]=0;
    for(int i=0;i<4;i++) d[root]=(d[root]+aa[i]*now.m[i][0])%mod;
    for(int i=0;i<4;i++) d_1[root]=(d_1[root]+aa[i]*now.m[i][1])%mod;
    for(int i=0;i<4;i++) sa[root]=(sa[root]+aa[i]*now.m[i][2])%mod;
}
void pushDown(int root,int le){
    if(mat_same(lazy[root],zzz,4))return;//不可少的剪枝.......
    lazy[root<<1]=mat_mul(lazy[root<<1],lazy[root],4);
    lazy[root<<1|1]=mat_mul(lazy[root<<1|1],lazy[root],4);
    upup(root<<1,le-(le>>1),lazy[root]);
    upup(root<<1|1,le>>1,lazy[root]);
    init_mat(lazy[root],4);//消除标记
}
void build(int l,int r,int root){
    for(int i=0;i<4;i++) lazy[root].m[i][i]=1;//lazy初始化为单位矩阵
    if(l==r){
        d[root]=fid(a[l],4);d_1[root]=fid(a[l]-1,4);sa[root]=a[l];
        return;
    }
    int m=(l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);
    pushUp(root);
}
void update(int L,int R,int l,int r,int root,mat mm){
    if(l>=L&&r<=R){
        lazy[root]=mat_mul(lazy[root],mm,4);//lazy是累乘的
        upup(root,r-l+1,mm);//当前区间更新
        return ;
    }
    pushDown(root,r-l+1);
    int m=(l+r)>>1;
    if(m>=L) update(L,R,l,m,root<<1,mm);
    if(m<R)  update(L,R,m+1,r,root<<1|1,mm);
    pushUp(root);
}
LL query(int L,int R,int l,int r,int root){
    if(l>=L&&r<=R)
        return d[root];
    pushDown(root,r-l+1);
    int m=(l+r)>>1;
    LL re=0;
    if(m>=L) re+=query(L,R,l,m,root<<1);
    if(m<R)  re+=query(L,R,m+1,r,root<<1|1);
    return re%mod;
}

int main(){
    memset(zzz.m,0,sizeof(zzz));
    for(int i=0;i<4;i++) zzz.m[i][i]=1;
    jin_mat_init();
    int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    build(1,n,1);
    int Q;scanf("%d",&Q);while(Q--){
        int op;scanf("%d",&op);
        if(op==1){
            int scaa,scbb,sccc;scanf("%d%d%d",&scaa,&scbb,&sccc);
            mat xxxx=mat_quick_mod(xx,sccc,4);//计算要乘的矩阵,乘到lazy里面...
            update(scaa,scbb,1,n,1,xxxx);
        }
        else{
            int scaa,scbb;scanf("%d%d",&scaa,&scbb);
            printf("%lld\n",query(scaa,scbb,1,n,1));
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值