JZOJ4724. 【NOIP2016提高A组模拟8.21】

本文介绍了一个结合Fibonacci数列特性和线段树数据结构的问题解决方法。通过对Fibonacci数列的操作及区间更新查询,利用线段树高效地处理数列的修改和求和操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

DJL为了避免成为一只咸鱼,来找czgj学习Fibonacci数列。
通过czgj的谆谆教导,DJL明白了Fibonacci数列是这样定义的:
F(1)=1;F(2)=1;F(n)=F(n-1)+F(n-2)(n>2)
Czgj深谙熟能生巧的道理,于是他给了DJL一个数列,并安排了如下的训练计划:
1、“1 L r”,表示给ai 加上F(i-L+1) ,其中L<=i<=r ;
2、“2 L r”,表示询问[l,r]中a的和,对1000000009取模。
DJL经过长时间的学习,感觉身体被掏空,他希望你能帮他解决这个问题。

Input

第一行两个整数n和m,表示原始数列的长度,和总的训练次数。
第二行n个整数a1,a2,…,an(1<=ai<=10^9) ,表示czgj给DJL的原始数列。
接下来m行,每一行给出三个整数,表示问题描述中的两种训练的一种。保证1<=L<=r<=n 。

Output

对于每一种形如“2 L r”的训练,输出一行一个整数值。

Sample Input

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

Sample Output

17
12
样例解释
经过第一次操作,数列变为a=[2,3,5,7] ;
第二次询问,sum=2+3+5+7=17 ;
经过第三次操作,数列变为a=[2,4,6,9] ;
第四次询问,sum=2+4+6=12 。

Data Constraint

对于20%的数据,1≤n, m≤100;
对于40%的数据,1≤n, m≤1000;
对于100%的数据,1≤n, m≤100000。
这里写图片描述

题解

题目后面给了很多提示,
其实用最简单的线段树就可以解决。
根据提示4,对于知道了开头第一个和第二个之后,
这个序列的第n项,已经前n项的和是很好求的。
线段树上面每个节点维护3个信息,
分别是当前区间序列开头第一个位置,开头第二个位置,已经这个节点的子树的序列和。
合并两个序列就只需要将第一个数和第二个数分别相加,显然是对的。

考虑如何查询,
如果整个区间都是被包含,就直接用这个点维护的和,
如果有交集,就将这个区间有交集部分统计进答案。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#define db double
#define ll long long
#define ls (x<<1)
#define rs (x<<1|1)
#define N 200008
#define M 103
#define G getchar
#define P putchar
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

const int mo=1000000009;
int n,m,cz,ans;
int f[N],s[N],a[N],sum[N];
int s1[N*4],s2[N*4],ss[N*4];
int opl,opr,l[N*4],r[N*4],p[N*4];
bool lazy[4*N];

int add(int y,int z){return (z+y>=mo?z+y-mo:z+y);}

void down(int x)
{
    if(!lazy[x])return;
    s1[ls]=s1[x];
    s2[ls]=s2[x];
    s1[rs]=add((ll)s1[x]*f[p[ls]-1]%mo,(ll)s2[x]*f[p[ls]]%mo);
    s2[rs]=add((ll)s1[x]*f[p[ls]]%mo,(ll)s2[x]*f[p[ls]+1]%mo);
    lazy[ls]=lazy[rs]=1;
    lazy[x]=0;
}

void ins(int x)
{
    if(opl<=l[x] && r[x]<=opr)
    {
        s1[x]=add(s1[x],f[l[x]-opl+1]);
        s2[x]=add(s2[x],f[l[x]-opl+2]);
        ss[x]=add(add(ss[ls],ss[rs]),add((ll)s1[x]*f[p[x]]%mo,(ll)s2[x]*s[p[x]-1]%mo));
        return;
    }
    int m=(l[x]+r[x])>>1;
    if(opl<=m)ins(ls);
    if(m<opr)ins(rs);
    ss[x]=add(add(ss[ls],ss[rs]),add((ll)s1[x]*f[p[x]]%mo,(ll)s2[x]*s[p[x]-1]%mo));
}

void find(int x)
{
    if(opl<=l[x] && r[x]<=opr)
    {
        ans=add(ans,ss[x]);
        return;
    }
    int tl=max(l[x],opl),tr=min(r[x],opr);
    ans=add(ans,add((ll)s1[x]*f[tr-l[x]+1]%mo,(ll)s2[x]*s[tr-l[x]]%mo));
    if(tl>l[x])tl--,ans=add(ans,mo-add((ll)s1[x]*f[tl-l[x]+1]%mo,(ll)s2[x]*s[tl-l[x]]%mo));
    int m=(l[x]+r[x])>>1;
    if(opl<=m)find(ls);
    if(m<opr)find(rs);
}

void build(int x,int L,int R)
{
    l[x]=L;r[x]=R;p[x]=(R-L+1);
    if(L==R)return;
    int m=(L+R)>>1;
    build(ls,L,m);
    build(rs,m+1,R);
}

int main()
{
    freopen("fibonacci.in","r",stdin);
    freopen("fibonacci.out","w",stdout);

    f[1]=f[2]=s[1]=1;s[2]=2;
    read(n);read(m);
    for(int i=1;i<=n;i++)read(a[i]),sum[i]=add(sum[i-1],a[i]);
    for(int i=3;i<=n+3;i++)
        f[i]=add(f[i-1],f[i-2]),s[i]=add(f[i],s[i-1]);

    build(1,1,n);

    for(int i=1;i<=m;i++)
    {
        read(cz);read(opl);read(opr);
        if(cz==1)ins(1);else
        {
            ans=sum[opr]-sum[opl-1];
            find(1);
            write(add(ans,mo));P('\n');
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值