zoukankan      html  css  js  c++  java
  • CF446C DZY Loves Fibonacci Numbers(广义斐波那契数列)

    题意

    给一个数列,支持区间修改和区间求和,区间修改操作给区间第(i)位加上(fib_i)

    思路

    如果使用线段树的话,显然区间和可以合并,考虑两个问题:

    1. 如何化区间修改为快速的打标记

    2. 打标记之后如何快速计算这个标记的贡献

    (f)表示斐波那契数列,定义广义斐波那契数列:(g_i=a imes f_{i-1} + b imes f_{i-2},(igeq 3),g_1=a,g_2=b),显然,对于(g)(g_i=g_{i-1}+g_{i-2})(乘法分配律)

    定理:$$sum_{i=1}^n{g_i} = g_{n+2} - g_2$$

    由数学归纳法容易证明:(n=1)显然成立,(ngeq 2)时,有$$sum_{i=1}^n{g_i} = sum_{i=1}^{n-1}{g_i} + g_n = (g_{n+1}-g_2) + g_n = g_{n+2} -g_2$$

    而根据(g)的定义,有$$g_{n+2} - g_2 = a imes f_n + b imes f_{n+1} - b$$

    显然一个斐波那契数列加上另一个斐波那契数列仍然是斐波那契数列(广义),由上式可以知道,区间和只与(a,b,n)有关,由于(a,b)(g_1,g_2)(n)为区间长度,所以只用知道区间的前两项就足够求出区间的和啦

    对于一个区间操作,只用给前两位打标记,求贡献用上面的公式即可;下传标记左区间直接下传,右区间算一下再下传

    P.S. 不用担心区间长度为1的情况,(g_1 = g_3 - g_2)依然成立

    Code

    #include<bits/stdc++.h>
    #define N 600005
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    using namespace std;
    typedef long long ll;
    const ll mod = 1000000009;
    int n,m,a[N];
    int k,L,R;
    ll sum[N<<2],f[N+100],tag1[N<<2],tag2[N<<2];
    
    template <class T>
    void read(T &x)
    {
    	char c; int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    void pushup(int rt) { sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod; }
    void add_sign(int rt,int l,int r,ll a,ll b)
    {
    	tag1[rt]=(tag1[rt]+a)%mod;
    	tag2[rt]=(tag2[rt]+b)%mod;
    	int len=(r-l+1);
    	sum[rt] = (sum[rt] + a*f[len]%mod + b*f[len+1]%mod - b)%mod;
    }
    void pushdown(int rt,int l,int r)
    {
    	if(!tag1[rt]&&!tag2[rt]) return;
    	int mid=(l+r)>>1;
    	int Llen=(mid-l+1);
    	ll rig1=(tag1[rt]*f[Llen-1]%mod + tag2[rt]*f[Llen]%mod)%mod;
    	ll rig2=(tag1[rt]*f[Llen]%mod + tag2[rt]*f[Llen+1]%mod)%mod;
    	add_sign(rt<<1,l,mid,tag1[rt],tag2[rt]);
    	add_sign(rt<<1|1,mid+1,r,rig1,rig2);
    	tag1[rt]=tag2[rt]=0;
    }
    void modify(int rt,int l,int r,int x,int y)
    {
    	if(x<=l&&r<=y) return add_sign(rt,l,r,f[l-L+1],f[l-L+2]);
    	int mid=(l+r)>>1;
    	pushdown(rt,l,r);
    	if(x<=mid) modify(rt<<1,l,mid,x,y);
    	if(y>mid) modify(rt<<1|1,mid+1,r,x,y);
    	pushup(rt);
    }
    ll query(int rt,int l,int r,int x,int y)
    {
    	if(x<=l&&r<=y) return sum[rt];
    	int mid=(l+r)>>1;
    	pushdown(rt,l,r);
    	ll ret=0;
    	if(x<=mid) ret=(ret+query(rt<<1,l,mid,x,y))%mod;
    	if(y>mid) ret=(ret+query(rt<<1|1,mid+1,r,x,y))%mod;
    	return ret;
    }
    void build(int rt,int l,int r)
    {
    	if(l==r) { sum[rt]=a[l]; return; }
    	int mid=(l+r)>>1;
    	build(rt<<1,l,mid);
    	build(rt<<1|1,mid+1,r);
    	pushup(rt);
    }
    void init()
    {
    	f[1]=f[2]=1;
    	for(int i=3;i<=n+10;++i) f[i]=(f[i-1]+f[i-2])%mod;
    }
    int main()
    {
    	read(n);read(m);init();
    	for(int i=1;i<=n;++i) read(a[i]);
    	build(1,1,n);
    	while(m--)
    	{
    		read(k);read(L);read(R);
    		if(k==2) printf("%lld
    ",(query(1,1,n,L,R)%mod+mod)%mod);
    		else modify(1,1,n,L,R);
    	}
    	return 0;
    }
    
  • 相关阅读:
    python给邮箱发送消息
    shell 的echo和 printf
    shell 基本运算符
    shell傳遞參數
    shell變量和數組
    pycharm的放大和缩小字体的显示 和ubunt的截圖工具使用 ubuntu上安装qq微信等工具
    flask的g对象
    mysqlcilent的安装
    Ubuntu安装 和 python开发
    使用python来建立http服务
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11736321.html
Copyright © 2011-2022 走看看