线段树维护等比数列

先上一道例题: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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #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<>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<=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<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<不明白的看一看代码。。 <p>翻完题解我发现这种做法已经是最简单的了。 <p>当然还有其他的做法 如:广义斐波那契数列,一些比较神奇的式子什么的。 <p>但是我不是太理解所以这里不再赘述可以放上[题解](https://www.luogu.com.cn/problemnew/solution/CF446C) <p>再简述一下一些性质 设Fn表示斐波那契数列的第n项 Sn为前n项的和。 <p>那么有 $S_n=F_{n+2}-F_2$ 证明不再证了很好搞的。 <p>考虑广义斐波那契$f_i=a\cdot f_1+b\cdot f_2$类似于这个形式?我不太清楚。。反正留意一点吧这个东西还是挺有用的。 <p>还有一个最鬼畜的性质$f_{n+m}=f_{n+1}f_m+f_{n}f_{m-1}$ <p>就这么多了 相信线段树维护等比数列你肯定已经会了!

posted @ 2020-03-06 23:31  chdy  阅读(539)  评论(0编辑  收藏  举报