E - Sasha and Array(线段树+矩阵快速幂)

Sasha has an array of integers a1, a2, ..., an. You have to perform m queries. There might be queries of two types:

  1. 1 l r x — increase all integers on the segment from l to r by values x;
  2. 2 l r — find , where f(x) is the x-th Fibonacci number. As this number may be large, you only have to find it modulo 109 + 7.

In this problem we define Fibonacci numbers as follows: f(1) = 1, f(2) = 1, f(x) = f(x - 1) + f(x - 2) for all x > 2.

Sasha is a very talented boy and he managed to perform all queries in five seconds. Will you be able to write the program that performs as well as Sasha?

Input

The first line of the input contains two integers n and m (1 ≤ n ≤ 100 000, 1 ≤ m ≤ 100 000) — the number of elements in the array and the number of queries respectively.

The next line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109).

Then follow m lines with queries descriptions. Each of them contains integers tpi, li, ri and may be xi (1 ≤ tpi ≤ 2, 1 ≤ li ≤ ri ≤ n, 1 ≤ xi ≤ 109). Here tpi = 1 corresponds to the queries of the first type and tpi corresponds to the queries of the second type.

It's guaranteed that the input will contains at least one query of the second type.

Output

For each query of the second type print the answer modulo 109 + 7.

Examples

Input

5 4
1 1 2 1 1
2 1 5
1 2 4 2
2 2 4
2 1 5

Output

5
7
9

Note

Initially, array a is equal to 1, 1, 2, 1, 1.

The answer for the first query of the second type is f(1) + f(1) + f(2) + f(1) + f(1) = 1 + 1 + 1 + 1 + 1 = 5.

After the query 1 2 4 2 array a is equal to 1, 3, 4, 3, 1.

The answer for the second query of the second type is f(3) + f(4) + f(3) = 2 + 3 + 2 = 7.

The answer for the third query of the second type is f(1) + f(3) + f(4) + f(3) + f(1) = 1 + 2 + 3 + 2 + 1 = 9.

题意:有n个数,每一个数代表斐波那契数列的下标,有2种操作:1:将区间L到R得数同时加上x。2:计算区间L到R的和。

题解:我们建立矩阵线段树,线段树的每个叶节点矩阵代表当前值的斐波那契矩阵,同时sum往上更新,lazy往下更新。

我们首先要知道:

F(n-1)     F(n-2)

F(n-2)     F(n-3)

乘上矩阵  P

1              1

1              0

得到:

F(n)        F(n-1)

F(n-1)     F(n-2)

那么规律矩阵就为P。

(   因为斐波那契矩阵中4个值全部满足斐波那契特性,故我们可以用线段树来优化。)

同时加上单位矩阵的辅助作用有以下规律

矩阵P的x-1次幂所得矩阵即为斐波那契矩阵X,(第一个数即为斐波那契数列的第x项)(这里可能有点绕,结合矩阵快速幂函数,举例带入即可理解)

此时矩阵快速幂就派上用场了,写一个求第x个斐波那契矩阵的函数。

由于矩阵全部为同一个矩阵P,(因为单位矩阵乘以任何矩阵,值都不变)

则操作1:区间L,R同时加上x,就相当于线段树上的L到R的每个节点矩阵同时乘上x个规律矩阵P。

则先将x个矩阵P先由矩阵快速幂算出来,再更新线段树即可,(同时要打lazy)

操作2:询问区间和只需带入线段树函数即可。

注意:不要担心左乘右乘的问题,这个问题由于矩阵为同一个矩阵,左乘右乘都行。

#include<iostream>
#include<cstring>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int N=100050;
const int mod = 1000000007;
int n,m,l,r,L,R;
ll w;
struct Mat
{
    ll mat[5][5];
    void init()  //初始化为单位矩阵
    {
        mat[1][1]=mat[2][2]=1;
        mat[1][2]=mat[2][1]=0;
    }
} sum[N<<2],lazy[N<<2];
Mat operator*(Mat a,Mat b)   //重载乘法
{
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=1; i<=2; i++)
    {
        for(int j=1; j<=2; j++)
        {
            for(int k=1; k<=2; k++)
            {
                c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
            }
        }
    }
    return c;
}
Mat operator + (Mat a,Mat b)  //重载加法
{
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=1; i<=2; i++)
    {
        for(int j=1; j<=2; j++)
        {
            c.mat[i][j]=(a.mat[i][j]+b.mat[i][j])%mod;
        }
    }
    return c;
}
Mat quickpow(ll m)  //矩阵快速幂,求得规律矩阵的m次的幂
{
    Mat res,a;
    res.init();  //res初始化为单位矩阵
    a.mat[1][1]=a.mat[1][2]=a.mat[2][1]=1; //a为规律矩阵
    a.mat[2][2]=0;
    while(m)
    {
        if(m&1)
            res=res*a;  //注意这里左乘右乘都行,因为矩阵是相同的矩阵
        a=a*a;
        m>>=1;
    }
    return res;
}
void pushup(int rt)
{
    sum[rt]=sum[rt*2]+sum[rt*2+1];
}
void pushdown(int rt)
{
    sum[rt*2]=sum[rt*2]*lazy[rt];
    sum[rt*2+1]=sum[rt*2+1]*lazy[rt];
    lazy[rt*2]=lazy[rt*2]*lazy[rt];
    lazy[rt*2+1]=lazy[rt*2+1]*lazy[rt];
    lazy[rt].init();
}
void build(int l,int r,int rt)
{
    sum[rt].init();
    lazy[rt].init();
    if(l==r)
    {
        ll x;
        scanf("%lld",&x);
        sum[rt]=quickpow(x-1);   //建立矩阵线段树
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    pushup(rt);
}
void update(int L,int R,int l,int r,int rt,Mat b)
{
    if(L<=l&&R>=r)
    {
        sum[rt]=sum[rt]*b;    //这里左乘右乘都可以
        lazy[rt]=lazy[rt]*b;
        return ;
    }
    pushdown(rt);
    int mid=(l+r)/2;
    if(L<=mid)                  //我觉得这样写逻辑更清晰
        update(L,R,l,mid,rt*2,b);
    if(R>=mid+1)
        update(L,R,mid+1,r,rt*2+1,b);
    pushup(rt);
}
ll querysum(int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)
        return sum[rt].mat[1][1];
    pushdown(rt);
    int mid=(l+r)/2;
    ll ans=0;
    if(L<=mid)
        ans=(ans+querysum(L,R,l,mid,rt*2))%mod;
    if(R>=mid+1)
        ans=(ans+querysum(L,R,mid+1,r,rt*2+1))%mod;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    build(1,n,1);
    for(int i=1; i<=m; i++)
    {
        int x;
        scanf("%d%d%d",&x,&L,&R);
        if(x==1)
        {
            scanf("%lld",&w);
            Mat temp=quickpow(w);
            update(L,R,1,n,1,temp);
        }
        else
        {
            cout<<querysum(L,R,1,n,1)<<endl;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824158/article/details/87703559
今日推荐