Codeforces 718C 线段树+矩乘

题意:

维护一个序列,支持两种操作:
1.区间[l,r]的权值+x
2.询问区间[l,r]的函数和,即∑fib(x)这里的函数即斐波那契函数
数据范围:1≤n,q≤105

思路:
一般求斐波那契函数的方法可以考虑矩阵乘法,这里也是这样的。
我们不用线段树维护权值,我们用线段树维护线段树维护区间矩阵和。
有一个矩阵乘法的性质:A*B+A*C=A*(B+C)
在求斐波那契数列中,是A*F,A是变换矩阵,F是列矩阵
那么我们用线段树的lazy标记维护A矩阵,然后用sum维护F矩阵
之后在线段树上,就变成了区间更新乘以x。

 

//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
const int mod=1000000007,N=100050;
int n,m,op,xx,yy,zz;
struct Matrix{
    int a[2][2];
    void init(){memset(a,0,sizeof(a));}
    void dia(){a[0][0]=a[1][1]=1,a[0][1]=a[1][0]=0;}
}sum[N<<3],lazy[N<<3],base;
Matrix operator*(Matrix a,Matrix b){
    Matrix c;c.init();
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
                c.a[i][j]=(1ll*a.a[i][k]*b.a[k][j]+c.a[i][j])%mod;
    return c;
}
Matrix operator+(Matrix a,Matrix b){
    Matrix c;c.init();
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            c.a[i][j]=(a.a[i][j]+b.a[i][j])%mod;
    return c;
}
Matrix operator^(Matrix a,int b){
    Matrix c;c.dia();
    while(b){
        if(b&1)c=c*a;
        a=a*a,b>>=1;
    }return c;
}
void push_up(int pos){sum[pos]=sum[pos<<1]+sum[pos<<1|1];}
void build(int l,int r,int pos){
    if(l==r){scanf("%d",&xx);sum[pos]=base^(xx-1);return;}
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    build(l,mid,lson),build(mid+1,r,rson);
    lazy[pos].dia(),push_up(pos);
}
void push_down(int pos,int lson,int rson){
    lazy[lson]=lazy[lson]*lazy[pos],sum[lson]=sum[lson]*lazy[pos];
    lazy[rson]=lazy[rson]*lazy[pos],sum[rson]=sum[rson]*lazy[pos];
    lazy[pos].dia();
}
Matrix query(int l,int r,int pos,int L,int R){
    if(l>=L&&r<=R)return sum[pos];
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    push_down(pos,lson,rson);
    if(mid<L)return query(mid+1,r,rson,L,R);
    else if(mid>=R)return query(l,mid,lson,L,R);
    else return query(l,mid,lson,L,R)+query(mid+1,r,rson,L,R);
}
void insert(int l,int r,int pos,int L,int R,Matrix wei){
    if(l>=L&&r<=R){sum[pos]=sum[pos]*wei;lazy[pos]=lazy[pos]*wei;return;}
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    push_down(pos,lson,rson);
    if(mid<L)insert(mid+1,r,rson,L,R,wei);
    else if(mid>=R)insert(l,mid,lson,L,R,wei);
    else insert(l,mid,lson,L,R,wei),insert(mid+1,r,rson,L,R,wei);
    push_up(pos);
}
int main(){
    base.a[0][0]=base.a[0][1]=base.a[1][0]=1;
    scanf("%d%d",&n,&m),build(1,n,1);
    while(m--){
        scanf("%d%d%d",&op,&xx,&yy);
        if(op==1)scanf("%d",&zz),insert(1,n,1,xx,yy,base^zz);
        else printf("%d\n",query(1,n,1,xx,yy).a[0][0]);
    }
}

 

转载于:https://www.cnblogs.com/SiriusRen/p/9330257.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值