SkyWT / 博客 / CodeForces 447E - DZY Loves Fibonacci Numbers 题解:线段树

CodeForces 447E - DZY Loves Fibonacci Numbers 题解:线段树

2019 年 1 月 29 日 12:01


文章目录

Description

Link

In mathematical terms, the sequence FnF_n of Fibonacci numbers is defined by the recurrence relation

F1=1;F2=1;Fn=Fn1+Fn2(n>2)F_1 = 1; F_2 = 1; F_n = F_{n - 1} + F_{n - 2} (n > 2)

DZY loves Fibonacci numbers very much. Today DZY gives you an array consisting of n integers: a1,a2,,ana_1, a_2, \dots, a_n. Moreover, there are mm queries, each query has one of the two types:

  1. Format of the query "1 l r". In reply to the query, you need to add Fil+1F_{i - l + 1} to each element aia_i, where lirl \leq i \leq r.
  2. Format of the query "2 l r". In reply to the query you should output the value of modulo 1000000009(109+9)1000000009 (10^9 + 9).

Help DZY reply to all the queries.

  • time limit per test: 4 seconds
  • memory limit per test: 256 megabytes
  • input: standard input
  • output: standard output

Input

The first line of the input contains two integers nn and m(1n,m300000)m (1 ≤ n, m ≤ 300000). The second line contains nn integers a1,a2,,ana_1, a_2, \dots, a_n (1ai1091 ≤ a_i ≤ 10^9) — initial array aa.

Then, mm lines follow. A single line describes a single query in the format given in the statement. It is guaranteed that for each query inequality 1lrn1 ≤ l ≤ r ≤ n holds.

Output

For each query of the second type, print the value of the sum on a single line.

Examples

input

4 4
1 2 3 4
1 1 4
2 1 4
1 2 4
2 1 3

output

17
12

Note

After the first query, a=[2,3,5,7]a = [2, 3, 5, 7].

For the second query, sum=2+3+5+7=17sum = 2 + 3 + 5 + 7 = 17.

After the third query, a=[2,4,6,9]a = [2, 4, 6, 9].

For the fourth query, sum=2+4+6=12sum = 2 + 4 + 6 = 12.

Translation

题意:给出一个数组 aia_i,现在有 mm 个操作,每个操作给出 L 和 R,可能是将这个区间里所有数字加上斐波那契数列对应的项(第 ii 个数字加 FiL+1F_{i-L+1}),或者是查询这个区间所有值之和。

Analysis

肯定想到用线段树。但是一般的线段树只能维护加和,需要考虑如何对斐波那契数列也构造 lazy tag。可以发现以下两个结论:

  • 如果只存数列的前两项 a 和 b,可以推出这个数列,包括可以 Θ(1)\Theta(1) 推出第 nn 项和前 nn 项之和。列表可以发现规律:
12345...n
aba+ba+2b2a+3b...Fn2a+Fn1bF_{n-2}a+F_{n-1}b

FiF_i 表示斐波那契数列第 ii 项)

  • 如何 Θ(1)\Theta(1) 求和?

Sum=i=1naFn2+bFn1=asumn2+bsumn1+aSum = \sum_{i=1}^{n} a\ast F_{n-2}+b\ast F_{n-1} = a\ast sum_{n-2} +b\ast sum_{n-1}+a

sumisum_i 表示斐波那契前 ii 项前缀和)

  • 这样的数列具有可加性,也就是 lazy tag 如果直接累计不会有问题。

这样我们可以写一个 Fibonacci 相关的模块:

namespace Fibonacci{
    int f[maxn],sum[maxn];

    inline void build(){
        f[1]=f[2]=1;sum[1]=1;sum[2]=2;
        for (int i=3;i<=N;i++) f[i]=(f[i-1]+f[i-2])%tt,sum[i]=(sum[i-1]+f[i])%tt;
    }

    inline int query(int a,int b,int n){
        if (n==0) return 0; else
        if (n==1) return a%tt; else
        if (n==2) return b%tt; else
        return (f[n-2]*a%tt+f[n-1]*b%tt)%tt;
    }

    inline int get_sum(int a,int b,int n){
        if (n==0) return 0; else
        if (n==1) return a%tt; else
        if (n==2) return (a+b)%tt; else
        return (a*sum[n-2]%tt+b*sum[n-1]%tt+a%tt)%tt;
    }
}

那么接下来我们可以写一个变异的线段树了。打两个烂标记 tag_a 和 tag_b 分别表示数列第一项和第二项。需要注意的是,update 操作时在向下“分割”的时候需要特别注意下数列左端点的两种情况。

建树是不需要的,可以假装原来的序列全是 0,询问的时候再前缀和累加。可以少写一个 build 了~

Code

写这题需要耐心……

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
#define int long long

const int maxn=300005,N=300000;
const int tt=1000000009;

inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}

inline void plus_mod(int &x,int y){
    x=(x+y)%tt;
}

namespace Fibonacci{
    int f[maxn],sum[maxn];

    inline void build(){
        f[1]=f[2]=1;sum[1]=1;sum[2]=2;
        for (int i=3;i<=N;i++) f[i]=(f[i-1]+f[i-2])%tt,sum[i]=(sum[i-1]+f[i])%tt;
    }

    inline int query(int a,int b,int n){
        if (n==0) return 0; else
        if (n==1) return a%tt; else
        if (n==2) return b%tt; else
        return (f[n-2]*a%tt+f[n-1]*b%tt)%tt;
    }

    inline int get_sum(int a,int b,int n){
        if (n==0) return 0; else
        if (n==1) return a%tt; else
        if (n==2) return (a+b)%tt; else
        return (a*sum[n-2]%tt+b*sum[n-1]%tt+a%tt)%tt;
    }
}

#define ls ((p<<1))
#define rs ((p<<1)+1)
#define mid (((tr-tl)>>1)+tl)

namespace SegmentTree{
    int sum[maxn*4];
    int tag_a[maxn*4],tag_b[maxn*4];

    inline void push_down(int tl,int tr,int p){
        int as=Fibonacci::query(tag_a[p],tag_b[p],mid+1-tl+1);
        int bs=Fibonacci::query(tag_a[p],tag_b[p],mid+2-tl+1);
        plus_mod(sum[ls],Fibonacci::get_sum(tag_a[p],tag_b[p],mid-tl+1));
        plus_mod(sum[rs],Fibonacci::get_sum(as,bs,tr-(mid+1)+1));
        plus_mod(tag_a[ls],tag_a[p]); plus_mod(tag_b[ls],tag_b[p]);
        plus_mod(tag_a[rs],as); plus_mod(tag_b[rs],bs);
        tag_a[p]=tag_b[p]=0;
    }

    inline void update(int sl,int sr,int tl,int tr,int p,int a,int b,int st){
        if (sl<=tl && tr<=sr){
            plus_mod(sum[p],Fibonacci::get_sum(a,b,tr-tl+1));
            plus_mod(tag_a[p],a); plus_mod(tag_b[p],b);
            return;
        }
        push_down(tl,tr,p);
        int as,bs;
        if (sl<=mid  ){
            update(sl,sr,tl,mid,ls,a,b,st);
            as=Fibonacci::query(a,b,mid+1-max(sl,tl)+1);
            bs=Fibonacci::query(a,b,mid+2-max(sl,tl)+1);
        } else {
            as=a;bs=b;
        }
        if (mid+1<=sr) update(sl,sr,mid+1,tr,rs,as,bs,mid+1);
        sum[p]=(sum[ls]+sum[rs])%tt;
    }
    
    inline int query(int sl,int sr,int tl,int tr,int p){
        if (sl<=tl && tr<=sr) return sum[p];
        push_down(tl,tr,p);
        int ret=0;
        if (sl<=mid  ) plus_mod(ret,query(sl,sr,tl,mid,ls));
        if (mid+1<=sr) plus_mod(ret,query(sl,sr,mid+1,tr,rs));
        return ret%tt;
    }
}

int n,m;
int a[maxn],sum[maxn];

signed main(){
    Fibonacci::build();
    n=read();m=read();
    for (int i=1;i<=n;i++) a[i]=read(),sum[i]=(sum[i-1]+a[i])%tt;
    for (int i=1;i<=m;i++){
        int opt=read(),x=read(),y=read();
        if (opt==1){
            SegmentTree::update(x,y,1,n,1,1,1,x);
        } else if (opt==2){
            int ans=SegmentTree::query(x,y,1,n,1);
            printf("%lld\n",(ans+sum[y]-sum[x-1]+tt)%tt);
        }
    }
    return 0;
}

颓了半个学期文化课,该回来颓 OI 了 :new_moon_with_face:

我的博客并没有弃坑!


暂无评论


发表新的评论

所有评论都将经过博主审核。请勿填写无意义邮箱或发表无关评论、广告等,否则会被视为垃圾评论。

提交评论即表明你同意本网站使用 Cookie,并允许本站在后台记录你的邮箱、IP 地址等必要信息。这些信息不会被透露给其他用户。(提交一次评论后,本提示将不再展示)