【挖坑記】 JZOJ 4724 斐波那契

題目大意

有一個斐波那契數列:F(1)=1;F(2)=1;F(n)=F(n-1)+F(n-2)(n>2);
也有一個序列長度爲n的A;
還有兩種操作:
1、“1 L r”,表示給ai 加上F(i-L+1) ,其中L<=i<=r ;
2、“2 L r”,表示詢問 這裏寫圖片描述的值。

n,m<=100000,a[i]<=1e9
時間限制4s
空間限制256M

解題思路

這題做得我滿腦子都是尼克楊的問號,一定要寫一寫。

如果區間修改的不是斐波那契數的話,就是很簡單的線段樹;而現在就要通過特別的方式來使用線段樹。

這棵線段樹的tag是一個二元組(a,b),表示要加上去的斐波那契數列的前兩項,至於怎麼用前兩項和區間長度快速求和呢,來看:

斐波那契第二項之後的數都是由前兩個數乘不同的係數得來的,所以我們可以先得出這兩個係數,就是說比如f[i]=f[1]*a+f[2]*b,我們先求a和b。
f[1]=1*f[1]+0*f[2];
f[2]=0*f[1]+1*f[2];
f[3]=1*f[1]+1*f[2];
f[4]=1*f[1]+2*f[2];
這樣推一下就會發現,f[1]和f[2]的係數也可以構成斐波那契數列,遞推預處理一下即可,對a和b分別做前綴和,就可以O(1)知道一個區間內有多少個f[1]和f[2];

至於線段樹的實現細節,我們來看一條結論:
對於類斐波那契數列H1=a, H2=b,滿足:
1、 Hn = a ∗ Fn−2 + b ∗ Fn−1
2、H1 + H2 + … + Hn = Hn+2 − b
用這個結合之前的前綴和可以直接求出H中的任意一項。
這一條在Pushdown的時候用得到。

最後貼代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll ding=1e9+9;

struct nod
{
    ll a,b;
} c[maxn*4];
int i,n,m,z,l,r,a[maxn];
ll s,f[3][maxn],pr[3][maxn],tr[maxn*4];
void pre(int n)
{
    f[1][1]=1,f[1][2]=0;
    f[2][1]=0,f[2][2]=1;
    pr[1][1]=1,pr[1][2]=1;
    pr[2][1]=0,pr[2][2]=1;
    int i,j;
    fr(i,3,n)
    {
        fr(j,1,2)
        {
            f[j][i]=(f[j][i-1]+f[j][i-2])%ding;
            pr[j][i]=(pr[j][i-1]+f[j][i])%ding;
        }
    }
    return;
}
void maket(int v,int l,int r)
{
    if (l==r)
    {
        tr[v]=a[l];
        return;
    }
    int m=(l+r) >> 1;
    maket(v+v,l,m);
    maket(v+v+1,m+1,r);
    tr[v]=(tr[v+v]+tr[v+v+1])%ding;
    return;
}
ll calc(ll a,ll b,int l)
{
    if (l==1) return a;
    return (pr[1][l]*a%ding+pr[2][l]*b%ding)%ding;
}
ll get(ll a,ll b,ll n)
{
    if (!n) return 0;
    if (n==1) return a;
    if (n==2) return b;
    if (n==3) return a+b;
    return (calc(a,b,n-2)+b)%ding;
}
void update(int v,int st,int en)
{
    if (!c[v].a && !c[v].b) return;
    ll a=c[v].a,b=c[v].b;
    int m=(st+en) >> 1;
    tr[v+v]=(tr[v+v]+calc(a,b,m-st+1))%ding;

    ll a_=get(a,b,m-st+2),b_=get(a,b,m-st+3);
    tr[v+v+1]=(tr[v+v+1]+calc(a_,b_,en-m))%ding;

    c[v+v].a=(c[v+v].a+a)%ding;
    c[v+v].b=(c[v+v].b+b)%ding;
    c[v+v+1].a=(c[v+v+1].a+a_)%ding;
    c[v+v+1].b=(c[v+v+1].b+b_)%ding;

    c[v].a=c[v].b=0;
    return;
}
void modify(int v,int st,int en,int l,int r,nod x)
{
    if (st==l && en==r)                                   
    {
        ll tt;
        tt=calc(x.a,x.b,en-st+1);
        tr[v]=(tr[v]+tt)%ding;
        c[v].a=(c[v].a+x.a)%ding;
        c[v].b=(c[v].b+x.b)%ding;
        return;
    }
    update(v,st,en);
    int m=(st+en) >> 1;
    if (r<=m) modify(v+v,st,m,l,r,x);
    else if (l>m) modify(v+v+1,m+1,en,l,r,x);
    else
    {
        nod vv;
        if (m-l+1>=2)
        {
            vv.a=get(x.a,x.b,m-l+2);
            vv.b=get(x.a,x.b,m-l+3);
        } else vv.a=x.b,vv.b=x.a+x.b;
        modify(v+v,st,m,l,m,x);
        modify(v+v+1,m+1,en,m+1,r,vv);
    }
    tr[v]=(tr[v+v]+tr[v+v+1])%ding;
    return;
}
void findd(int v,int st,int en,int l,int r)
{
    if (st==l && en==r)
    {
        s=(s+tr[v])%ding;
        return;
    }
    update(v,st,en);
    int m=(st+en) >> 1;
    if (r<=m) findd(v+v,st,m,l,r);
    else if (l>m) findd(v+v+1,m+1,en,l,r);
    else
    {
        findd(v+v,st,m,l,m);
        findd(v+v+1,m+1,en,m+1,r);
    }
    tr[v]=(tr[v+v]+tr[v+v+1])%ding;
    return;
}
int main()
{
    freopen("czgj3.in","r",stdin);
    freopen("thi.out","w",stdout);
    scanf("%d%d",&n,&m);
    pre(n);
    fr(i,1,n) scanf("%d",&a[i]);
    maket(1,1,n);
    fr(i,1,m)
    {
        scanf("%d%d%d",&z,&l,&r);
        if (z==1)
        {
            nod t;
            t.a=1,t.b=1;
            modify(1,1,n,l,r,t);
        } else
        {
            s=0;
            findd(1,1,n,l,r);
            printf("%lld\n",s);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章