线段树维护等比数列

先上一道例题:CF446C
维护区间求和 和区间对应位置加上对应的斐波那契数列。
这不是一个一次函数,也不是一个差分序列。所以我们线段树不能做区间加这一个操作。

考虑斐波那契数列的通项 \(Fn=\frac{\sqrt{5}}{5}[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n]\)

发现这个东西是一个等比数列 我们可以利用线段树来维护等比数列 当然由于存在模数1e9+9

那么\(\sqrt{5}\)这个东西就好处理了。

简述一下二次剩余:存在一个式子\(x^2\equiv n(mod p)\) 给出n和p 求出一个x满足这个等式 则我们称n为模p的二次剩余。
若不存在这样的x则称n是模p的非二次剩余。同时我们称x为二次同余方程的解。

对于一个n如果我们要求\(\sqrt{n}mod p\)的值 那么么我们按n是否是模p的二次剩余如果是的话就会满足\(x^2\equiv n(mod p)\to \sqrt{n} mod p\)

那么我们就可以用x代替\(\sqrt{n}\)即只需要求出该二次同余方程的解即可。

综上对于上述题目 我们找到关于5的二次同余方程的解再除以一波逆元可以发现变成了两个等比数列。

我们直接线段树维护等比数列即可。关于区间加我们等比数列求和更新该区间的值。

考虑下放标记其实我们找到对应的区间加上左端点的等比数列的比该pushdown的时候把右边区间加上左边区间的贡献即可。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define db double
#define INF 1000000000
#define ld long double
#define pb push_back
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(p,n,i) for(ll i=p;i<=n;++i)
#define pii pair<ll,ll> 
#define F first
#define S second
#define mk make_pair
#define EPS 1e-7
#define P 13331ll
#define mod 1000000009
#define ll long long
#define l(p) t[p].l
#define r(p) t[p].r
#define sum(p) t[p].sum
#define tag1(p) t[p].tag1
#define tag2(p) t[p].tag2
#define zz p<<1
#define yy p<<1|1
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=300010,s5=383008016;
ll n,m;
ll inv2,inv5,q1,q2,s,in1,in2;
ll v1[MAXN],v2[MAXN],a[MAXN];
struct wy
{
    int l,r;
    ll sum;
    ll tag1,tag2;
}t[MAXN<<2];
inline void build(ll p,ll l,ll r)
{
    l(p)=l;r(p)=r;
    if(l==r)return;
    ll mid=(l+r)>>1;
    build(zz,l,mid);
    build(yy,mid+1,r);
}
inline ll ksm(ll b,ll p)
{
    ll cnt=1;
    while(p)
    {
        if(p&1)cnt=cnt*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return cnt;
}
inline void pushdown(ll p)
{
    ll mid=(l(p)+r(p))>>1,len=mid-l(p)+1;
    tag1(zz)=(tag1(zz)+tag1(p))%mod;
    tag2(zz)=(tag2(zz)+tag2(p))%mod;
    ll w1=v1[len]*tag1(p)%mod,w2=v2[len]*tag2(p)%mod;
    tag1(yy)=(tag1(yy)+w1)%mod;
    tag2(yy)=(tag2(yy)+w2)%mod;
    sum(zz)=(sum(zz)+tag1(p)*(v1[len+1]-q1)%mod*in1)%mod;
    sum(zz)=(sum(zz)-tag2(p)*(v2[len+1]-q2)%mod*in2)%mod;
    //cout<<(sum(zz)*s%mod+mod)%mod<<endl;
    len=r(p)-mid;
    sum(yy)=(sum(yy)+w1*(v1[len+1]-q1)%mod*in1)%mod;
    sum(yy)=(sum(yy)-w2*(v2[len+1]-q2)%mod*in2)%mod;
    //cout<<(sum(yy)*s%mod+mod)%mod<<endl;
    tag1(p)=tag2(p)=0;
}
inline void change(ll p,ll l,ll r,ll v,ll vv)
{
    if(l(p)==l&&r==r(p))
    {
        ll ww=r-l+1;//求q^1~q^ww+1次方
        tag1(p)=(tag1(p)+v)%mod;
        tag2(p)=(tag2(p)+vv)%mod;
        sum(p)=(sum(p)+v*(v1[ww+1]-q1)%mod*in1)%mod;
        sum(p)=(sum(p)-vv*(v2[ww+1]-q2)%mod*in2)%mod;
        //cout<<(sum(p)*s%mod+mod)%mod<<endl;
        return;
    }
    ll mid=(l(p)+r(p))>>1;
    if(tag1(p)||tag2(p))pushdown(p);
    if(r<=mid){change(zz,l,r,v,vv);sum(p)=(sum(zz)+sum(yy))%mod;return;}
    if(l>mid){change(yy,l,r,v,vv);sum(p)=(sum(zz)+sum(yy))%mod;return;}
    ll len=mid-l+1;
    change(zz,l,mid,v,vv);change(yy,mid+1,r,v*v1[len]%mod,vv*v2[len]%mod);
    sum(p)=(sum(zz)+sum(yy))%mod;
    //cout<<(t[p].sum*s%mod+mod)%mod<<endl;
}
inline ll ask(ll p,ll l,ll r)
{
    if(l<=l(p)&&r>=r(p))return sum(p);
    ll mid=(l(p)+r(p))>>1;
    ll cnt=0;
    if(tag1(p)||tag2(p))pushdown(p);
    if(l<=mid)cnt=ask(zz,l,r);
    //cout<<(cnt*s%mod+mod)%mod<<endl;
    if(r>mid)cnt+=ask(yy,l,r);
    return cnt%mod;
}
signed main()
{
    freopen("1.in","r",stdin);
    n=read();m=read();
    //for(ll i=1;;++i)if(i*i%mod==5){cout<<i<<endl;break;}
    //cout<<383008016ll*383008016ll%mod<<endl;
    inv2=ksm(2,mod-2);inv5=ksm(5,mod-2);
    build(1,1,n);v1[0]=v2[0]=1;
    q1=(s5+1)*inv2%mod;q2=(1-s5+mod)*inv2%mod;
    s=s5*inv5%mod;
    //cout<<s5*s5%mod<<endl;
    in1=ksm(q1-1,mod-2);
    in2=ksm(q2-1,mod-2);
    //cout<<(q1-q2+mod)*s%mod<<endl;
    for(int i=1;i<=n+1;++i)
    {
        v1[i]=v1[i-1]*q1%mod,v2[i]=v2[i-1]*q2%mod;
        if(i<=n)a[i]=(read()+a[i-1])%mod;
    }
    for(int i=1;i<=m;++i)
    {
        int op,x,y;
        op=read();x=read();y=read();
        if(op==1)change(1,x,y,1,1);
        else printf("%d\n",((s*ask(1,x,y)+a[y]-a[x-1])%mod+mod)%mod);
    }
    return 0;
}

不明白的看一看代码。。

猜你喜欢

转载自www.cnblogs.com/chdy/p/12431709.html
今日推荐