【Codeforces 718C&719E】Sasha and Array【线段树成段更新+矩阵快速幂】

题意:

给你一个数列,有两种操作1 l r x 给[l,r]区间上的数加上x, 2 l r 询问[l,r]区间fibonacci数列的和(f[l]+f[l+1]+……f[r])

题解:

这样的区间加和区间询问很容易想到线段树成段更新,关键是怎么存储信息,我们都知道fibonacci数列可以用矩阵快速幂递推,基础矩阵是a[0][0]=1,a[0][1]=1,a[1][0]=1,a[1][1]=0,a[0][0]中存储的就是当前的fibonacci值(不会这个可以先做下poj3070)

然后我们可以在线段树的每个节点都存一个2*2的矩阵,这个矩阵就是当前fibonacci数列的运算矩阵,这是线段树的建树过程,

现在我们来看操作1 [l,r]区间上的数加上x 我们设A[x]矩阵代表基础矩阵乘上x次之后形成的矩阵(这里提下在上一条建树操作的时候数值为1的矩阵应该是单位矩阵,数值y大于等于1的存的应该是A[y-1]) 给区间数加上x就相当于给区间所有矩阵乘上一个A[x] 也即A[x]*(A[l]+A[l+1]+……+A[r]) 这样1操作可以用lazy的思想直接对整段进行更新

然后看操作2 我们只要对[l,r]中存的矩阵中[0][0]位置的数求和就是答案

注意:

线段树操作时所有的lazy,sum都用2*2的矩阵来存,并且lazy操作时矩阵是相乘而不是想加(本人在这儿错了好久)

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define PB push_back
#define MP make_pair
#define ll __int64
#define MS(a,b) memset(a,b,sizeof(a))
#define LL (rt<<1)
#define RR (rt<<1|1)
#define lson l,mid,LL
#define rson mid+1,r,RR
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lb(x) (x&(-x))
using namespace std;
void In(){freopen("in.in","r",stdin);}
void Out(){freopen("out.out","w",stdout);}
const int N=1e5+10;
const int M=1e6+10;
const int Mbit=1e6+10;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
struct Matrix
{
    ll mat[2][2];
    int n;
    Matrix(){}
    Matrix(int _n)
    {
        n=_n;
        for(int i=0;i<n;i++)for(int j=0;j<n;j++)mat[i][j]=0;
    }
    void zero(int _n)
    {
        n=_n;
        for(int i=0;i<n;i++)for(int j=0;j<n;j++)mat[i][j]=0;
    }
    void unit(int _n)
    {
        n=_n;
        for(int i=0;i<n;i++)for(int j=0;j<n;j++)mat[i][j]=(i==j);
    }
    Matrix operator +(const Matrix &b)const
    {
        Matrix ret=Matrix(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                ret.mat[i][j]=(mat[i][j]+b.mat[i][j])%mod;
        return ret;
    }
    Matrix operator *(const Matrix &b)const
    {
        Matrix ret = Matrix(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                for(int k=0;k<n;k++)
                    (ret.mat[i][j]+=mat[i][k]*b.mat[k][j])%=mod;
        return ret;
    }
};
Matrix mul(Matrix s,int n)
{
    Matrix ans;
    ans.unit(s.n);
    while(n){
        if(n&1)ans=ans*s;
        s=s*s;
        n>>=1;
    }
    return ans;
}
struct node
{
    Matrix lazy,sum;
    bool flag;
}tree[N<<2];
Matrix fibo(int x)
{
    Matrix s;
    s.n=2;
    s.mat[0][0]=1;s.mat[0][1]=1;
    s.mat[1][0]=1;s.mat[1][1]=0;
    return mul(s,x-1);
}
Matrix fibo1(int x)
{
    Matrix s;
    s.n=2;
    s.mat[0][0]=1;s.mat[0][1]=1;
    s.mat[1][0]=1;s.mat[1][1]=0;
    return mul(s,x);
}
void up(int rt)
{
    tree[rt].sum=tree[LL].sum+tree[RR].sum;
}
void down(int rt)
{
    if(tree[rt].flag){
        tree[LL].lazy=tree[LL].lazy*tree[rt].lazy;
        tree[RR].lazy=tree[RR].lazy*tree[rt].lazy;
        tree[LL].sum=tree[LL].sum*tree[rt].lazy;
        tree[RR].sum=tree[RR].sum*tree[rt].lazy;
        tree[LL].flag=tree[RR].flag=1;
        tree[rt].flag=0;tree[rt].lazy.unit(2);
    }
}
void build(int l,int r,int rt)
{
    if(l==r){
        int x;scanf("%d",&x);
        tree[rt].sum=fibo(x);
        tree[rt].lazy.unit(2);
        tree[rt].flag=0;
        return;
    }
    tree[rt].sum.zero(2);
    tree[rt].lazy.unit(2);
    tree[rt].flag=0;
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    up(rt);
}
void update(int L,int R,int l,int r,int rt,Matrix x)
{
    if(L<=l&&r<=R){
        tree[rt].lazy=tree[rt].lazy*x;
        tree[rt].flag=1;
        tree[rt].sum=tree[rt].sum*x;
        return;
    }
    down(rt);
    int mid=(l+r)/2;
    if(L<=mid) update(L,R,lson,x);
    if(R>mid) update(L,R,rson,x);
    up(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)return tree[rt].sum.mat[0][0];
    int mid=(l+r)/2;
    down(rt);
    ll ans=0;
    if(L<=mid)ans+=query(L,R,lson);
    if(R>mid) ans+=query(L,R,rson);
    up(rt);
    return ans%mod;
}
int main()
{
    int n,m,op,l,r,x;
    while(~scanf("%d%d",&n,&m)){
        build(1,n,1);
        while(m--){
            scanf("%d%d%d",&op,&l,&r);
            if(op==1){
                scanf("%d",&x);
                update(l,r,1,n,1,fibo(1+x));
            }
            else printf("%I64d\n",query(l,r,1,n,1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值