华南理工大学“三七互娱杯”程序设计竞赛-B: HRY and fibonacci(线段树+矩阵快速幂)

链接:https://ac.nowcoder.com/acm/contest/874/B

时间限制:C/C++ 5秒,其他语言10秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
HRY is a pupil. He has just learned about Fibonacci sequence recently, and he made some new sequences :

f i c ( n ) = ∑ i = 1 n f i b ( i ) fic(n)=\sum_{i=1}^nfib(i) fic(n)=i=1nfib(i) f i d ( n ) = ∑ i = 1 n f i c ( i ) fid(n)=\sum_{i=1}^nfic(i) fid(n)=i=1nfic(i)

fib(i) means the ith element in the Fibonacci sequence :

f i b ( 1 ) = f i b ( 2 ) = 1 , f i b ( i ) = f i b ( i − 1 ) + f i b ( i − 2 ) , i ≥ 3 fib(1)=fib(2)=1,fib(i)=fib(i−1)+fib(i−2),i≥3 fib(1)=fib(2)=1,fib(i)=fib(i1)+fib(i2),i3

At first this problem is to calculate fid(n), but that is too easy. So after discussing with 10256, they changed the problem:
Given a positive integer array a 1 , a 2 , . . . , a n , a_1,a_2,...,a_n, a1,a2,...,an, perform the following two types of operations:

  1. Given l,r,x, for all l ≤ i ≤ r l≤i≤r lir, execute a i = a i + x a_i=a_i+x ai=ai+x.
  2. Given l,r, calculate ( ∑ i = l r f i d ( a i ) ) % 100000007. (\sum_{i=l}^rfid(a_i))\%100000007. (i=lrfid(ai))%100000007.

输入描述:
The first line of the input contains an integer n, indicating the length of the array.

The second line contains n positive integers separated by spaces, indicating a 1 , a 2 , . . . , a n − 1 , a n . a_1,a_2,...,a_{n−1},a_n. a1,a2,...,an1,an.

The third line contains an integer Q, indicating the number of operations.

For the next Q lines, it is either 1 1 1 l l l r r r x x x, indicating the first type of operation, or 2 2 2 l l l r r r, indicating the second type of operation.

All values in input are integers.
1 ≤ n , Q ≤ 100000 , 1 ≤ a i , x ≤ 1 0 9 , 1 ≤ l ≤ r ≤ n . 1≤n,Q≤100000,1≤a_i,x≤10^9,1≤l≤r≤n. 1n,Q100000,1ai,x109,1lrn.

输出描述:
For each operation of second type, output ( ∑ i = l r f i d ( a i ) ) % 100000007. (\sum_{i=l}^rfid(a_i))\%100000007. (i=lrfid(ai))%100000007.
示例1
输入
3
1 2 3
6
2 1 3
1 1 3 1
2 1 3
1 1 1 2
1 2 2 3
2 1 3
输出
11
24
74

思路:由斐波那契数列性质 f 1 + f 2 + f 3 + . . + f n = f n + 2 − 1 f_1+f_2+f_3+..+f_n=f_{n+2}-1 f1+f2+f3+..+fn=fn+21,可得 f i d ( n ) = f n + 4 − n − 3 fid(n)=f_{n+4}-n-3 fid(n)=fn+4n3.

知道了 f i d ( n ) fid(n) fid(n)的公式,那么 f i d ( n ) fid(n) fid(n)可以由矩阵快速幂求得,即:
[ f 1 0 f 0 − 1 0 0 0 0 0 0 0 0 0 0 0 0 ] ∗ [ 1 0 1 0 0 2 0 1 1 0 0 0 0 − 1 0 0 ] n + 3 = [ f n + 4 n + 3 f n + 3 n + 2 0 0 0 0 0 0 0 0 0 0 0 0 ] \left[ \begin{matrix} f_1& 0&f_0&-1\\ 0& 0&0&0\\ 0& 0&0&0\\ 0&0&0&0 \end{matrix} \right]* \left[ \begin{matrix} 1& 0&1&0\\ 0& 2&0&1\\ 1& 0&0&0\\ 0&-1&0&0 \end{matrix} \right] ^{n+3}= \left[ \begin{matrix} f_{n+4}& n+3&f_{n+3}&n+2\\ 0& 0&0&0\\ 0& 0&0&0\\ 0&0&0&0 \end{matrix} \right] f10000000f000010001010020110000100n+3=fn+4000n+3000fn+3000n+2000

现在就是考虑区间求和,由于有区间修改,所以我们可以建立一个线段树,但线段树节点存储的是伴随矩阵 A = [ 1 0 1 0 0 2 0 1 1 0 0 0 0 − 1 0 0 ] A=\left[ \begin{matrix} 1& 0&1&0\\ 0& 2&0&1\\ 1& 0&0&0\\ 0&-1&0&0 \end{matrix} \right] A=1010020110000100

即每个叶节点存储的是 A a i A^{a_i} Aai
那么区间修改的的时候只需要在区间 [ L , R ] [L,R] [L,R]乘上相应的 A x A^x Ax即可,打上懒惰标记。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MOD=100000007;
typedef long long ll;
struct M
{
    int a[4][4];
    M()
    {
        memset(a,0,sizeof a);
        for(int i=0;i<4;i++)a[i][i]=1;
    }
}now;
M operator+(const M&A,const M&B)
{
    M C;
    for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)C.a[i][j]=(A.a[i][j]+B.a[i][j])%MOD;
    return C;
}
M operator*(const M&A,const M&B)
{
    M C;memset(C.a,0,sizeof C.a);
    for(int i=0;i<4;i++)
    for(int j=0;j<4;j++)
    for(int k=0;k<4;k++)
    {
        C.a[i][j]+=1ll*A.a[i][k]*B.a[k][j]%MOD;
        C.a[i][j]%=MOD;
    }
    return C;
}
M POW(int n)
{
    M a,res;
    memset(res.a,0,sizeof res.a);
    memset(a.a,0,sizeof a.a);
    for(int i=0;i<4;i++)res.a[i][i]=1;
    a.a[0][0]=a.a[0][2]=1;
    a.a[1][1]=2,a.a[1][3]=1;
    a.a[2][0]=1;
    a.a[3][1]=MOD-1;
    while(n)
    {
        if(n&1)res=res*a;
        a=a*a;
        n>>=1;
    }
    return res;
}
struct lenka
{
    int l,r;
    M a,lazy;
}A[MAX<<2];
void build(int k,int l,int r)
{
    A[k].l=l,A[k].r=r;
    A[k].lazy=M();
    if(l==r)
    {
        int x;
        scanf("%d",&x);
        A[k].a=POW(x+3);
        return;
    }
    build(2*k,l,(l+r)/2);
    build(2*k+1,(l+r)/2+1,r);
    A[k].a=A[2*k].a+A[2*k+1].a;
}
void add(int k,int x,int y,M z)
{
    if(x==A[k].l&&y==A[k].r)
    {
        A[k].a=A[k].a*z;
        if(x!=y)A[k].lazy=A[k].lazy*z;
        return;
    }
    if(y<=A[2*k].r)add(2*k,x,y,z);
    else if(x>=A[2*k+1].l)add(2*k+1,x,y,z);
    else
    {
        add(2*k,x,A[2*k].r,z);
        add(2*k+1,A[2*k+1].l,y,z);
    }
    A[k].a=A[k].lazy*(A[2*k].a+A[2*k+1].a);
}
M ask(int k,int x,int y)
{
    if(x==A[k].l&&y==A[k].r)return A[k].a;
    if(y<=A[2*k].r)return ask(2*k,x,y);
    else if(x>=A[2*k+1].l)return ask(2*k+1,x,y);
    return A[k].lazy*(ask(2*k,x,A[2*k].r)+ask(2*k+1,A[2*k+1].l,y));
}
int main()
{
    memset(now.a,0,sizeof now.a);
    now.a[0][0]=1;
    now.a[0][3]=MOD-1;
    int n;
    cin>>n;
    build(1,1,n);
    int m;
    cin>>m;
    while(m--)
    {
        int op,x,y,z;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)
        {
            scanf("%d",&z);
            add(1,x,y,POW(z));
        }
        else
        {
            M ans=now*ask(1,x,y);
            printf("%d\n",(ans.a[0][0]-ans.a[0][1]+MOD)%MOD);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值