CodeForces 718C Sasha and Array(矩阵类+线段树)

来源:互联网 发布:golang 代码安全 泄密 编辑:程序博客网 时间:2024/06/13 23:24
Sasha and Array
time limit per test:5 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

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 froml to r by valuesx;
  2. 2 l r — find , wheref(x) is the x-th Fibonacci number. As this number may be large, you only have to find it modulo109 + 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 allx > 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 andm (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 integerstpi,li,ri and may bexi (1 ≤ tpi ≤ 2,1 ≤ li ≤ ri ≤ n,1 ≤ xi ≤ 109). Heretpi = 1 corresponds to the queries of the first type andtpi 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 41 1 2 1 12 1 51 2 4 22 2 42 1 5
Output
579
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.



        虽然说矩阵快速幂不是本题的主要算法,但在这题却发挥着举足轻重的作用。

        大致题意就是,给出一组数列,有区间加和区间求和两种操作。但是不同的是,这里的求和不是单纯的数字相加,而是Fibonacci数列的第ai项,即Fibonacci数列对应项的总和。如果用普通的区间更新,增加操作非常简单,但是区间的Fibonacci数列对应项之和却不好求。这个时候矩阵就派上了用场。

        我们知道,FIbonacci数列的第i项可以通过矩阵快速幂求得,而矩阵既有乘法也有加法,所以既可以做到修改,也可以做到求和。修改的话,对于一个区间其对应项数加x,相当于整个区间乘以转移矩阵x次方。而求和的话,就是直接维护矩阵之和。如此一来,线段树中的和sum、lazytag,这些都得是矩阵类型的,然后会涉及到加、乘和乘方,故我们直接把矩阵写成一个矩阵类,使得程序更加的美观。纯线段树代码部分几乎看不出与普通的有什么区别。这里要注意因为修改做的是乘法的修改,所以初始化lazy和lazy下传之后,lazy不是变成0,而是变成单位矩阵。具体细节见代码:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstdlib>#include<cstring>#include<cmath>#include<iomanip>#include<vector>#include<queue>#define LL long long#define N (int) 1e5+10#define mod 1000000007using namespace std;struct matrix//矩阵类,包含加法、乘法、乘方{LL a[3][3];void init(){for(int i=1;i<3;i++)for(int j=1;j<3;j++)a[i][j]=0;}friend matrix operator *(matrix x,matrix y)    {        matrix res; res.init();        for(int i=1;i<3;i++)            for(int j=1;j<3;j++)            {                for(int k=1;k<3;k++)                    res.a[i][j]+=(x.a[i][k]*y.a[k][j])%mod;                res.a[i][j]%=mod;            }        return res;    }    friend matrix operator ^(matrix x,LL y)    {        matrix res;        if (y==0)        {            for(int i=1;i<3;i++)                for(int j=1;j<3;j++)                    res.a[i][j]=0;            for(int i=1;i<3;i++) res.a[i][i]=1;            return res;        } else while ((y&1)==0) {y>>=1;x=x*x;}        res=x; y>>=1;        while (y!=0)        {            x=x*x;            if ((y&1)!=0) res=res*x;            y>>=1;        }        return res;    }    friend matrix operator +(matrix x,matrix y)    {        matrix res;        for(int i=1;i<3;i++)            for(int j=1;j<3;j++)                res.a[i][j]=(x.a[i][j]+y.a[i][j])%mod;        return res;    }    friend bool operator !=(matrix x,matrix y)    {        for(int i=1;i<3;i++)            for(int j=1;j<3;j++)                if (x.a[i][j]!=y.a[i][j]) return 1;        return 0;    }} x,basic;int a[N],n,m;struct ST{struct node{matrix sum,lazy;int l,r;} tree[N<<2];inline void push_up(int i){tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;//区间Fibonacci数列和}inline void build(int i,int l,int r){tree[i].r=r;tree[i].l=l;tree[i].lazy=basic;//初始化为单位矩阵if (l==r){    tree[i].sum=x^(a[l]-1);//初始值return;}LL mid=(l+r)>>1;build(i<<1,l,mid);build(i<<1|1,mid+1,r);push_up(i);}inline void push_down(int i){tree[i<<1].lazy=tree[i<<1].lazy*tree[i].lazy;tree[i<<1|1].lazy=tree[i<<1|1].lazy*tree[i].lazy;//lazy是以乘法下传tree[i<<1].sum=tree[i<<1].sum*tree[i].lazy;tree[i<<1|1].sum=tree[i<<1|1].sum*tree[i].lazy;tree[i].lazy=basic;//下传后初始化为基本矩阵}inline void update(int i,int l,int r,matrix x){if ((tree[i].l==l)&&(tree[i].r==r)){tree[i].sum=tree[i].sum*x;tree[i].lazy=tree[i].lazy*x;return;}if (tree[i].lazy!=basic) push_down(i);LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) update(i<<1,l,r,x);else if (mid<l) update(i<<1|1,l,r,x);else{update(i<<1,l,mid,x);update(i<<1|1,mid+1,r,x);}push_up(i);}inline matrix getsum(int i,int l,int r){if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].sum;if (tree[i].lazy!=basic) push_down(i);LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getsum(i<<1,l,r);else if (mid<l) return getsum(i<<1|1,l,r);else return getsum(i<<1,l,mid)+getsum(i<<1|1,mid+1,r);}} seg;int main(){    x.a[1][1]=1;    x.a[1][2]=1;    x.a[2][1]=1;    basic.a[1][1]=basic.a[2][2]=1;//初始化转移矩阵和基本矩阵    while(~scanf("%d%d",&n,&m))    {        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        seg.build(1,1,n);        while(m--)        {            int l,r,op,delta;            scanf("%d%d%d",&op,&l,&r);            if (op==1)            {                scanf("%d",&delta);                seg.update(1,l,r,x^delta);//每次区间乘上转移矩阵的delta次方            } else            {                matrix ans=seg.getsum(1,l,r);                printf("%lld\n",ans.a[1][1]);            }        }    }    return 0;}