codeforces316E3 Summer Homework(线段树,斐波那契数列)

题目大意

给定一个n个数的数列,m个操作,有三种操作:

\(1\ x\ v\) 将\(a_x\)的值修改成v

\(2\ l\ r\\) 求 \(\sum_{i=l}^r x_i*f_{i-l}\) 其中对于\(f\)数组 \(f_0=1\ ,f_1=1\ ,f_i=f_{i-1}+f_{i-2}\) (就是斐波那契数列)

\(3\ l\ r\ x\\) 让\(a_i+x,i\in[l,r]\)

其中\(n\le 100000,m\le 100000\)

一看这个题QwQ,就知道是线段树题

QwQ那么怎么维护节点信息和合并区间呢

来举个栗子试一下

对于系数分别为\(1\ 1,1\ 2\)来说

将二者相加变为\(2\ 3\)也就是以第三个元素开头的系数序列了~

由于fib序列相加还是fib序列

哇,那这么说,这个题目所给的求和的操作,也是可以通过已知矩阵乘转移矩阵快速得到目标矩阵

那么!就可以通过这个东西来转移了!

\[
\begin{bmatrix}
0 & 1 \1& 1 \\end{bmatrix}
\]

所以!对于左右区间来说,合并的时候,只需要把右区间乘上左区间的长度次方(就相当于把右边这个区间的变为\(f_{mid-l+1}\)项开头)

同时,我们发现要进行矩阵转移,必须记录当前这个区间的元素从\(f_0\)开始和\(f_1\)开始的两个值,才能够进行矩阵计算

QwQ因为我不会矩阵乘法呀!

所以我是选择手动展开了矩阵的n次方

最后假设是求矩阵的n次方的话

那么最终的矩阵应为

\[
\begin{bmatrix}
{fib}_{n-2} & {fib}_{n-1} \{fib}_{n-1}& {fib}_{n} \\end{bmatrix}
\]

~只需要预处理一下fib序列和fib序列的前缀和就行了

void up(int root)
{
    ll len = f[2*root].len;
    f[root].len=(f[2*root].len+f[2*root+1].len)%mod;
    f[root].fir=(f[2*root].fir+f[2*root+1].fir*get(len-2)+f[2*root+1].sec*fib[len-1])%mod;
    f[root].sec=(f[2*root].sec+f[2*root+1].fir*fib[len-1]+f[2*root+1].sec*fib[len])%mod;
}

接着,我们考虑,对3操作

如果让一个区间加x,就是让这个区间加x*fib前缀和的区间长度-1项(因为\(f_0=1\))(求答案的是从\(f_0\)开始乘)

emmmm所以也是可以直接做了咯(记得从1开始乘的那个信息需要-add[root])

void pushdown(int root,int l,int r)
{
    if (add[root])
    {
        add[2*root]=(add[2*root]+add[root])%mod;
        add[2*root+1]=(add[2*root+1]+add[root])%mod;
        f[2*root].fir=(f[2*root].fir+add[root]*sum[f[2*root].len-1])%mod;
        f[2*root].sec=(f[2*root].sec+add[root]*sum[f[2*root].len]-add[root])%mod;
        f[2*root+1].fir=(f[2*root+1].fir+add[root]*sum[f[2*root+1].len-1])%mod;
        f[2*root+1].sec=(f[2*root+1].sec+add[root]*sum[f[2*root+1].len]-add[root])%mod; //之所以-1是因为要减掉fib[0]
        add[root]=0;
    }
}

update和change和build都差不多~

需要注意的是!!!!!!!!!

query的时候,不能直接\(return\ f[root].fir\)

因为如果让区间为\([l,r]\),就需要将这一段嫁接到\([x,l-1]\)的后面,对,所以也需要想之前合并的时候那样乘一个fib

if (x<=l && r<=y)
    {
        int len = l-1-x+1;
        if (len==0) return f[root].fir;
        return f[root].fir*get(len-2)+f[root].sec*get(len-1);
    }

其他的都差不多了啦

直接上代码!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#define ll long long

using namespace std;

inline ll read()
{
  ll x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();}
  while (isdigit(ch)) {x=x*10+ch-‘0‘;ch=getchar();}
  return x*f;
}

const int maxn = 2e5+1e2;
const int mod = 1000000000;

struct Node
{
    ll fir,sec,len;
};

Node f[4*maxn];
ll add[4*maxn];
ll a[maxn];
ll fib[maxn],sum[maxn];
int n,m;

void init()
{
    fib[0]=1,fib[1]=1;
    for (int i=2;i<=n;i++) fib[i]=(fib[i-1]+fib[i-2])%mod;
    sum[0]=1;
    for (int i=1;i<=n;i++) sum[i]=(sum[i-1]+fib[i])%mod;
}

ll get(int x)
{
    if (x<0) return 0;
    else return fib[x];
}

void up(int root)
{
    ll len = f[2*root].len;
    f[root].len=(f[2*root].len+f[2*root+1].len)%mod;
    f[root].fir=(f[2*root].fir+f[2*root+1].fir*get(len-2)+f[2*root+1].sec*fib[len-1])%mod;
    f[root].sec=(f[2*root].sec+f[2*root+1].fir*fib[len-1]+f[2*root+1].sec*fib[len])%mod;
}

void pushdown(int root,int l,int r)
{
    if (add[root])
    {
        add[2*root]=(add[2*root]+add[root])%mod;
        add[2*root+1]=(add[2*root+1]+add[root])%mod;
        f[2*root].fir=(f[2*root].fir+add[root]*sum[f[2*root].len-1])%mod;
        f[2*root].sec=(f[2*root].sec+add[root]*sum[f[2*root].len]-add[root])%mod;
        f[2*root+1].fir=(f[2*root+1].fir+add[root]*sum[f[2*root+1].len-1])%mod;
        f[2*root+1].sec=(f[2*root+1].sec+add[root]*sum[f[2*root+1].len]-add[root])%mod; //之所以-1是因为要减掉fib[0]
        add[root]=0;
    }
}

void build(int root,int l,int r)
{
    if (l==r)
    {
        f[root].sec=f[root].fir=a[l]%mod;
        f[root].len=1;
        return;
    }
    int mid = (l+r) >> 1;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    up(root);
}

void update(int root,int l,int r,int x,int y,int p)
{
    if (x<=l && r<=y)
    {
        add[root]=(add[root]+p)%mod;
        f[root].fir=(f[root].fir+sum[r-l]*p)%mod;
        f[root].sec=(f[root].sec+sum[r-l+1]*p-p)%mod;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) update(2*root,l,mid,x,y,p);
    if (y>mid) update(2*root+1,mid+1,r,x,y,p);
    up(root);
}

void change(int root,int l,int r,int x,int p)
{
    if (l==r)
    {
        f[root].fir=f[root].sec=p%mod;
        add[root]=0;
        f[root].len=1;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) change(2*root,l,mid,x,p);
    if (x>mid) change(2*root+1,mid+1,r,x,p);
    up(root);
}

ll query(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        int len = l-1-x+1;
        if (len==0) return f[root].fir;
        return f[root].fir*get(len-2)+f[root].sec*get(len-1);
    }
    int mid = (l+r) >> 1;
    pushdown(root,l,r);
    ll ans=0;
    if (x<=mid) ans=(ans+query(2*root,l,mid,x,y))%mod;
    if (y>mid) ans=(ans+query(2*root+1,mid+1,r,x,y))%mod;
    return ans%mod;
}

int main()
{
  scanf("%d%d",&n,&m);
  init();
  for (int i=1;i<=n;i++) a[i]=read();
  build(1,1,n);
  //cout<<query(1,1,n,1,4)<<endl;
  for (int i=1;i<=m;i++)
  {
     int opt;
     opt=read();
     if (opt==1)
     {
        int x=read();
        ll y=read();
        change(1,1,n,x,y);
       }
    if (opt==2)
    {
        int x=read(),y=read();
        //cout<<x<<" "<<y<<endl;
        printf("%lld\n",query(1,1,n,x,y));
        //cout<<query(1,1,n,1,4)<<endl;
    }
    if (opt==3)
    {
        int x=read(),y=read();
        ll z=read();
        update(1,1,n,x,y,z);
    }
  }
  return 0;
}

原文地址:https://www.cnblogs.com/yimmortal/p/10160713.html

时间: 2024-10-11 10:23:07

codeforces316E3 Summer Homework(线段树,斐波那契数列)的相关文章

【CF446C】DZY Loves Fibonacci Numbers (线段树 + 斐波那契数列)

Description ? 看题戳我 给你一个序列,要求支持区间加斐波那契数列和区间求和.\(~n \leq 3 \times 10 ^ 5, ~fib_1 = fib_2 = 1~\). Solution ? 先来考虑一段斐波那契数列如何快速求和,根据性质有 \[ \begin {align} fib_n &= fib_{n - 1} + fib_{n - 2} \ &= fib_ {n - 2} + fib_{n - 3} + fib_{n - 2} \ &= fib_{n -

[莫队算法 线段树 斐波那契 暴力] Codeforces 633H Fibonacci-ish II

题目大意:给出一个长度为n的数列a. 对于一个询问lj和rj.将a[lj]到a[rj]从小到大排序后并去重.设得到的新数列为b,长度为k,求F1*b1+F2*b2+F3*b3+...+Fk*bk.当中F为斐波那契数列.F1=F2=1.对每一个询问输出答案模m. 区间查询离线 用莫队算法 开棵权值线段树,然后用斐波那契的性质update F(n+m)=F(n+1)*F(m)+F(n)*F(m-1); #include<cstdio> #include<cstdlib> #includ

Benelux Algorithm Programming Contest 2014 Final ACM-ICPC Asia Training League 暑假第一阶段第二场 E. Excellent Engineers-单点更新、区间最值-线段树 G. Growling Gears I. Interesting Integers-类似斐波那契数列-递推思维题

先写这几道题,比赛的时候有事就只签了个到. E. Excellent Engineers 传送门: 这个题的意思就是如果一个人的r1,r2,r3中的某一个比已存在的人中的小,就把这个人添加到名单中. 因为是3个变量,所以按其中一个变量进行sort排序,然后,剩下的两个变量,一个当位置pos,一个当值val,通过线段树的单点更新和区间最值操作,就可以把名单确定. 代码: 1 //E-线段树 2 #include<iostream> 3 #include<cstdio> 4 #incl

coderfoces446c (斐波那契数列)

题目描述: 区间增值,但是每一项增加的值为Fi - l + 1,F[i]为斐波那契数列,求区间和? 考虑线段树,刚开始想用斐波那契数列的前n项和,可是推不出来,考虑到每个区间的增值序列都是一段斐波那契数列,他们的和是否有什么特性呢? 发现如果前两项为a和b的话,那么,a,b,a+b,a+2b,2a+3b,3a+5b; a和b前的系数为斐波那契数列(后一项为前两项之和, 设F[k]表示以a,b开头的第k项的值,s[k]代表以a和b开头的前k项和 F[k]=a*f[k-2]+b*f[k-1]; F[

树上三角形(斐波那契数列神奇应用)

树上三角形(斐波那契数列神奇应用) Description给定一个大小为 n 的有点权树,需要支持两个操作.0:询问(u,v),能否在 u 到 v 的简单路径上取三个点,使这三个点的点权作为边长可以构成一个三角形.1:修改某个点的点权. Input第一行两个整数 n,q 表示树的点数和操作数.第二行 n 个整数表示 n 个点的初始的点权.接下来 n-1 行,每行两个整数 a,b,表示 a 是 b 的父亲.接下来 q 行,每行三个整数 op,a,b:若 op=0,则表示询问(a,b).若 op=1

斐波那契数列和反向计算问题

反向计算:编写一个函数将一个整型转换为二进制形式 反向计算问题,递归比循环更简单 分析:需要理解,奇数的二进制最后一位是1,偶数的二进制最后一位一定是0,联想记忆,这个和整型的奇偶性是一致的,1本身就是奇数,0本身是偶数. 十进制整数转换为二进制整数采用"除2取余,逆序排列"法. 具体做法是:用2整除十进制整数,可以得到一个商和余数,再用2去除商,又会得到一个商和余数,如此进行,直到商为0时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列

斐波那契数列——摘自搜狗百科

1数列公式 递推公式 斐波那契数列:0.1.1.2.3.5.8.13.21.34.55.89.144... 如果设F(n)为该数列的第n项(n∈N*),那么这句话可以写成如下形式: F(0) = 0,F(1)=F(2)=1,F(n)=F(n-1)+F(n-2) (n≥3) 通项公式 通项公式的推导方法一:利用特征方程 线性递推数列的特征方程为: X^2=X+1 解得 X1=(1+√5)/2, X2=(1-√5)/2. 斐波拉契数列则F(n)=C1*X1^n + C2*X2^n ∵F(1)=F(2

斐波那契数列的实现(简单递归和动态规划)

斐波那契数列的实现(简单递归和动态规划) 一.简单递归的实现 1 #include "stdafx.h" 2 #include <string> 3 using namespace std; 4 int f(int n) 5 { 6 if (n == 0) 7 { 8 return 0; 9 } 10 if (n == 1) 11 { 12 return 1; 13 } 14 return f(n - 1) + f(n - 2); 15 } 16 int _tmain(in

斐波那契数列实例讲解以及C++实现

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.--在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)在现代物理.准晶体结构.化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963起出版了以<斐波纳契数列季刊>为名的一份数学杂志,用于专门刊载这方面的研究成果. 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,