Codeforces 446C - DZY Loves Fibonacci Numbers(斐波那契数列+线段树)
你可能会疑惑我为什么要写 *2400 的题的题解
首先一个很明显的想法是,看到斐波那契数列和 \(10^9+9\) 就想到通项公式,\(F_i=\dfrac{1}{\sqrt{5}}((\dfrac{1+\sqrt{5}}{2})^n-(\dfrac{1-\sqrt{5}}{2})^n)\)。并且 \(5\) 在模 \(10^9+9\) 意义下的二次剩余存在,为 \(383008016\)。
我们建两棵线段树分别维护展开式中 \((\dfrac{1+\sqrt{5}}{2})^n\) 和 \((\dfrac{1-\sqrt{5}}{2})^n\) 的部分,查询的时候原本的 \(a_i\) 可以做个前缀和 \(\mathcal O(1)\) 加上,其余部分直接在两棵线段树上区间查询相减并乘个 \(\dfrac{1}{\sqrt{5}}\) 即可。区间加操作相当于在两棵线段树区间 \([l,r]\) 加等比数列,这个可以用线段树区间加等比数列的套路维护。具体来说,我们以 \((\dfrac{1+\sqrt{5}}{2})^n\) 为例,线段树每个区间 \([L,R]\) 的懒标记 \(lz\) 表示该区间中第 \(i\in [L,R]\) 项的值要增加 \((\dfrac{1+\sqrt{5}}{2})^{i-L}\),然后你预处理 \(s_i=\sum\limits_{j=0}^{i-1}(\dfrac{1+\sqrt{5}}{2})^j\) 就可以 \(\mathcal O(1)\) 下放懒标记了。时间复杂度线对。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int SQRT_5=383008016;
const int INV2=5e8+5;
const int MAXN=3e5;
const int MOD=1e9+9;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,qu,INV_SQRT_5,a[MAXN+5],ss[MAXN+5];
struct segtree{
int base,pw[MAXN+5],sum[MAXN+5];
void prework(){
pw[0]=sum[0]=1;
for(int i=1;i<=n;i++) pw[i]=1ll*pw[i-1]*base%MOD;
for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+pw[i])%MOD;
}
struct node{int l,r,sum,lz;} s[MAXN*4+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushup(int k){s[k].sum=(s[k<<1].sum+s[k<<1|1].sum)%MOD;}
void pushdown(int k){
if(s[k].lz){
s[k<<1].lz=(s[k<<1].lz+s[k].lz)%MOD;
s[k<<1].sum=(s[k<<1].sum+1ll*s[k].lz*sum[s[k<<1].r-s[k<<1].l])%MOD;
s[k<<1|1].lz=(s[k<<1|1].lz+1ll*s[k].lz*pw[s[k<<1].r-s[k<<1].l+1])%MOD;
s[k<<1|1].sum=(s[k<<1|1].sum+1ll*s[k].lz*sum[s[k<<1|1].r-s[k<<1|1].l]%MOD*pw[s[k<<1].r-s[k<<1].l+1])%MOD;
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].sum=(s[k].sum+1ll*x*sum[s[k].r-s[k].l])%MOD;
s[k].lz=(s[k].lz+x)%MOD;return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,1ll*x*pw[mid-l+1]%MOD);
pushup(k);
}
int query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].sum;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%MOD;
}
} s1,s2;
int main(){
INV_SQRT_5=qpow(SQRT_5,MOD-2);scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),ss[i]=(ss[i-1]+a[i])%MOD;
s1.base=1ll*(SQRT_5+1)*INV2%MOD;s2.base=1ll*(1-SQRT_5+MOD)*INV2%MOD;
s1.prework();s2.prework();s1.build(1,1,n);s2.build(1,1,n);
while(qu--){
int opt,l,r;scanf("%d%d%d",&opt,&l,&r);
if(opt==1) s1.modify(1,l,r,s1.base),s2.modify(1,l,r,s2.base);
else printf("%d\n",((ss[r]-ss[l-1]+MOD)%MOD+1ll*(s1.query(1,l,r)-s2.query(1,l,r)+MOD)*INV_SQRT_5%MOD)%MOD);
}
return 0;
}
/*
4 4
1 2 3 4
1 1 4
2 1 4
2 1 2
2 3 4
*/
当然我之所以写这个题解是因为还有别的做法。
上面的做法用到了模数是 \(10^9+9\) 的性质,倘若模数不是 \(10^9+9\) 那岂不就歇菜了?
考虑斐波那契数列的一个性质 \(F_n=F_{n-m}F_{m-1}+F_{n-m+1}F_m\)。
那么我们就有 \(F_{i-l+1}=F_iF_{-l}+F_{i+1}F_{-l+1}\)
这里我们定义负数下标的斐波那契数列为:\(F_{-1}=F_1-F_0=1,F_{-2}=F_0-F_{-1}=-1,F_{-3}=F_{-1}-F_{-2}=2\),以此类推。
显然 \(F_{-i}=(-1)^{i+1}F_i\)
至于为什么等式 \(F_n=F_{n-m}F_{m-1}+F_{n-m+1}F_m\) 对负数下标的斐波那契数列同样适用,可用跷跷板归纳法证明,这里就不再赘述了。
知道这个性质之后,考虑将代求的数列拆成两部分,\(a_i=F_ib_i+F_{i+1}c_i\)。那么一次区间修改操作相当于令 \([l,r]\) 中的 \(b_i\) 加上 \(F_{-l}\),\(c_i\) 加上 \(F_{-l+1}\)。于是问题转化为对于数列 \(A_i=B_iC_i\) 进行两种操作,将区间 \([l,r]\) 中的 \(C_i\) 加上 \(v\),求区间 \([l,r]\) 中所有 \(A_i\) 的和。这个可以用线段树维护,线段树上区间 \([L,R]\) 的懒标记 \(lz\) 表示 \([L,R]\) 中的 \(C_i\) 要加上 \(lz\)。考虑线段树每个区间记录一个 \(sum\) 表示 \(\sum\limits_{i=L}^RB_i\),这样可做到 \(\mathcal O(1)\) 下放懒标记,时间复杂度还是线对。
btw P5138 也是用的这个套路,既然这题写了题解那题就不写了罢
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=3e5;
const int MOD=1e9+9;
int n,qu,a[MAXN+5],fib[MAXN+5],fib_neg[MAXN+5],sum[MAXN+5];
struct node{int l,r,sum1,sum2,val1,val2,lz1,lz2;} s[MAXN*4+5];
void pushup(int k){
s[k].val1=(s[k<<1].val1+s[k<<1|1].val1)%MOD;
s[k].val2=(s[k<<1].val2+s[k<<1|1].val2)%MOD;
}
void pushdown(int k){
if(s[k].lz1||s[k].lz2){
s[k<<1].val1=(s[k<<1].val1+1ll*s[k<<1].sum1*s[k].lz1)%MOD;
s[k<<1].val2=(s[k<<1].val2+1ll*s[k<<1].sum2*s[k].lz2)%MOD;
s[k<<1].lz1=(s[k<<1].lz1+s[k].lz1)%MOD;
s[k<<1].lz2=(s[k<<1].lz2+s[k].lz2)%MOD;
s[k<<1|1].val1=(s[k<<1|1].val1+1ll*s[k<<1|1].sum1*s[k].lz1)%MOD;
s[k<<1|1].val2=(s[k<<1|1].val2+1ll*s[k<<1|1].sum2*s[k].lz2)%MOD;
s[k<<1|1].lz1=(s[k<<1|1].lz1+s[k].lz1)%MOD;
s[k<<1|1].lz2=(s[k<<1|1].lz2+s[k].lz2)%MOD;
s[k].lz1=s[k].lz2=0;
}
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r){s[k].sum1=fib[l];s[k].sum2=fib[l+1];return;}
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
s[k].sum1=(s[k<<1].sum1+s[k<<1|1].sum1)%MOD;
s[k].sum2=(s[k<<1].sum2+s[k<<1|1].sum2)%MOD;
}
void modify(int k,int l,int r,int v1,int v2){
if(l<=s[k].l&&s[k].r<=r){
s[k].lz1=(s[k].lz1+v1)%MOD;s[k].lz2=(s[k].lz2+v2)%MOD;
s[k].val1=(s[k].val1+1ll*s[k].sum1*v1%MOD)%MOD;
s[k].val2=(s[k].val2+1ll*s[k].sum2*v2%MOD)%MOD;
return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,v1,v2);
else if(l>mid) modify(k<<1|1,l,r,v1,v2);
else modify(k<<1,l,mid,v1,v2),modify(k<<1|1,mid+1,r,v1,v2);
pushup(k);
}
int query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return (s[k].val1+s[k].val2)%MOD;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%MOD;
}
int main(){
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=(sum[i-1]+a[i])%MOD;
fib[1]=fib[2]=1;fib_neg[1]=1;fib_neg[2]=MOD-1;
for(int i=3;i<=n+1;i++){
fib[i]=(fib[i-1]+fib[i-2])%MOD;
if(~i&1) fib_neg[i]=MOD-fib[i];
else fib_neg[i]=fib[i];
} build(1,1,n);
while(qu--){
int opt,l,r;scanf("%d%d%d",&opt,&l,&r);
if(opt==1) modify(1,l,r,fib_neg[l],fib_neg[l-1]);
else printf("%d\n",((sum[r]-sum[l-1]+MOD)%MOD+query(1,l,r))%MOD);
}
return 0;
}
当然此题还有可能有别的方法,不过由于我太懒了就不写了/wq
Codeforces 446C - DZY Loves Fibonacci Numbers(斐波那契数列+线段树)的更多相关文章
- Codeforces 446-C DZY Loves Fibonacci Numbers 同余 线段树 斐波那契数列
C. DZY Loves Fibonacci Numbers time limit per test 4 seconds memory limit per test 256 megabytes inp ...
- codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)(两种方法)
In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation F1 ...
- codeforces 446C DZY Loves Fibonacci Numbers 数论+线段树成段更新
DZY Loves Fibonacci Numbers Time Limit:4000MS Memory Limit:262144KB 64bit IO Format:%I64d &a ...
- Codeforces 446C —— DZY Loves Fibonacci Numbers(线段树)
题目:DZY Loves Fibonacci Numbers 题意比較简单,不解释了. 尽管官方的题解也是用线段树,但还利用了二次剩余. 可是我没有想到二次剩余,然后写了个感觉非常复杂度的线段树,还是 ...
- ACM学习历程—Codeforces 446C DZY Loves Fibonacci Numbers(线段树 && 数论)
Description In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence ...
- codeforces 446C DZY Loves Fibonacci Numbers 线段树
假如F[1] = a, F[2] = B, F[n] = F[n - 1] + F[n - 2]. 写成矩阵表示形式可以很快发现F[n] = f[n - 1] * b + f[n - 2] * a. ...
- [Amazon] Program for Fibonacci numbers 斐波那契数列
The Fibonacci numbers are the numbers in the following integer sequence. 0, 1, 1, 2, 3, 5, 8, 13, 21 ...
- Codeforces 446C DZY Loves Fibonacci Numbers [线段树,数论]
洛谷 Codeforces 思路 这题知道结论就是水题,不知道就是神仙题-- 斐波那契数有这样一个性质:\(f_{n+m}=f_{n+1}f_m+f_{n}f_{m-1}\). 至于怎么证明嘛-- 即 ...
- 10、end关键字和Fibonacci series: 斐波纳契数列
# Fibonacci series: 斐波纳契数列 # 两个元素的总和确定了下一个数 a, b = 0, 1 #复合赋值表达式,a,b同时赋值0和1 while b < 10: print(b ...
随机推荐
- sql递归查询部门数据
1 with cte as 2 ( 3 select a.DepartCode,a.DepartName,a.ParentDepartCode from tbDeparts a where Paren ...
- Java:异常小记
Java:异常小记 对 Java 中的 异常 ,做一个微不足道的小小小小记 Error 和 Exception 相同点: Exception 和Error 都是继承了 Throwable 类,在 Ja ...
- [Beta]the Agiles Scrum Meeting 5
会议时间:2020.5.19 20:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 issue yjy 为评测机增加更多评测指标 评测部分增加更多评测指标 tq 为评测机增加更多评测指标 ...
- Noip模拟32(再度翻车) 2021.8.7
T1 Smooth 很水的一道题...可是最傻 的是考场上居然没有想到用优先队列优化... 上来开题看到这个,最一开始想,这题能用模拟短除法,再一想太慢了,就想着优化 偏偏想到线性筛然后试别的素 ...
- DC综合与Tcl语法结构概述
转载:https://www.cnblogs.com/IClearner/p/6617207.html 1.逻辑综合的概述 synthesis = translation + logic optimi ...
- 数组中只出现过一次的数字 牛客网 剑指Offer
数组中只出现过一次的数字 牛客网 剑指Offer 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了偶数次.请写程序找出这两个只出现一次的数字. def FindNumsAppearOnce ...
- python3.5 安装mysqlclient
python 3.5 安装 mysqlclient 会失败 pip install mysqlclient 注意这里环境中只有python3.5 会出现一大堆红字 编译终止, error: comma ...
- Codeforces Global Round 16题解
E. Buds Re-hanging 对于这个题该开始还是没想法的,但这显然是个思维题,还是要多多动手推样例,实践一下. 简化题意:给定一个有根树,规定某个点为树干,当且仅当这个点不是根,且这个点至少 ...
- cf13C Sequence(DP)
题意: N个数.a1...aN. 对于每个数而言,每一步只能加一或减一. 问最少总共需要多少步使得新序列是非递减序列. N (1 ≤ N ≤ 5000) 思路: *一个还不知道怎么证明的结论(待证): ...
- Mac搭建以太坊私有链
记录过程与问题 一.安装 以go版本的ethereum进行安装 brew tap ethereum/ethereum brew install ethereum # 如果希望基于ethereum的de ...