Codeforces Round #373 (Div. 2) E. Sasha and Array

 
E. 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 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.

题意:

给定一个数组a[],有两种操作:1 l r x —— a[l]~a[r]每个元素加x

2 l r—— 求 , 其中f 为fibonacci数列

题解:

线段树成段更新,lazy标记。

线段树基础算法参考:http://blog.csdn.net/zhou_yujia/article/details/51386549

重点在于求fibonacci数列。

, f[]表示fibonacci数列可以利用矩阵快速幂求出。

, 则f[i]=(F^n).mat[0][1];

当然

于是可以构造线段树,线段树的叶子节点不是某个实数,而是一个矩阵 (F^ai)

操作 1 l r x ,成段更新时

a数组[l,r] 每个元素加x,即对应线段树[l,r]每个叶子节点矩阵 乘 (F^x)

可以设定一个数组add[]表示懒惰,设定另一个矩阵数组lazy[]用于pushdown实际操作。

剩下的就是典型的线段树成段更新lazy标记的内容了。

#include <algorithm>
#include <cstring>
#include <string.h>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <cstdio>
#include <cmath>

#define LL long long
#define N 100005
#define INF 0x3ffffff

using namespace std;

const int mod = 1e9+7;

struct Matrix{
    int m[2][2];
    void init(){
        memset(m,0,sizeof(m));
    }
    Matrix operator+(const Matrix&a)const
    {
        Matrix ret;
        ret.m[0][0]=(m[0][0]+a.m[0][0])%mod;
        ret.m[0][1]=(m[0][1]+a.m[0][1])%mod;
        ret.m[1][0]=(m[1][0]+a.m[1][0])%mod;
        ret.m[1][1]=(m[1][1]+a.m[1][1])%mod;
        return ret;
    }
    Matrix operator*(const Matrix&a)
    {
        Matrix ret;
        ret.m[0][0]=(1ll*m[0][0]*a.m[0][0]+1ll*m[0][1]*a.m[1][0])%mod;
        ret.m[1][0]=ret.m[0][1]=(1ll*m[0][0]*a.m[0][1]+1ll*m[0][1]*a.m[1][1])%mod;
        ret.m[1][1]=(1ll*m[1][0]*a.m[0][1]+1ll*m[1][1]*a.m[1][1])%mod;
        return ret;
    }
};

int n,m;
Matrix tree[N<<2];
int add[N<<2];            //add[]表示懒惰标记
Matrix lazy[N<<2];      //成段更新时实际使用的lazy矩阵
Matrix I;                //单位矩阵
Matrix F;              //Fibonacci矩阵

void init()
{
    F.init();
    I.init();
    F.m[0][0]=F.m[1][0]=F.m[0][1]=1;
    I.m[0][0]=I.m[1][1]=1;
}

Matrix pow(int x)   //矩阵快速幂
{
    Matrix ret=I;
    Matrix tmp=F;
    while(x>0)
        {
            if(x&1) ret=ret*tmp;
            tmp=tmp*tmp;
            x>>=1;
        }
    return ret;
}
void build_tree(int rt,int l,int r)
{
    lazy[rt]=I;
    if(l==r){
        int a;
        scanf("%d",&a);
        if(a==1) tree[rt]=I;
        else tree[rt]=pow(a-1);
        return;
    }
    int mid=l+r>>1;
    build_tree(rt<<1,l,mid);
    build_tree(rt<<1|1,mid+1,r);
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

void pushDown(int rt)   
{
    if(add[rt])
    {
        add[rt<<1]+=add[rt];
        tree[rt<<1]=tree[rt<<1]*lazy[rt];
        lazy[rt<<1]=lazy[rt<<1]*lazy[rt];
        add[rt<<1|1]+=add[rt];
        tree[rt<<1|1]=tree[rt<<1|1]*lazy[rt];
        lazy[rt<<1|1]=lazy[rt<<1|1]*lazy[rt];
        add[rt]=0;
        lazy[rt]=I;
    }
}
void update(int rt,int l,int r,int L ,int R,int v)  //表示对区间[L,R]内的每个数均加v,rt是根节点
//  实际对应线段树区间[L,R]每个节点的矩阵乘 pow(v)
{ if(L<=l&&r<=R) { Matrix P=pow(v); //数组元素+v, 对应线段树相应节点矩阵 * pow(v) tree[rt]=tree[rt]*P; lazy[rt]=lazy[rt]*P; add[rt]+=v; return; } pushDown(rt); int mid=l+r>>1; if(L<=mid)update(rt<<1,l,mid,L,R,v); if(mid<R)update(rt<<1|1,mid+1,r,L,R,v); tree[rt]=tree[rt<<1]+tree[rt<<1|1]; } Matrix query(int rt,int l,int r,int L,int R) { if(L<=l&&r<=R) { return tree[rt]; } int mid=l+r>>1; pushDown(rt); Matrix ret; ret.init(); if(L<=mid) ret=ret+query(rt<<1,l,mid,L,R); if(mid<R) ret=ret+query(rt<<1|1,mid+1,r,L,R); return ret; } int main() { scanf("%d%d",&n,&m); init(); build_tree(1,1,n); while(m--) { int op,l,r,v; scanf("%d%d%d",&op,&l,&r); if(op==1){ scanf("%d",&v); update(1,1,n,l,r,v); } else{ Matrix ans=query(1,1,n,l,r); printf("%d ",ans.m[0][0]); } } return 0; }
原文地址:https://www.cnblogs.com/smartweed/p/5904385.html