Codeforces Round #FF (Div. 1) C (Segment_Tree)

题目

题意

给定一个长度为n的序列,对序列定义两种操作:
( 1 ,   l ,   r ) (1,\, l,\, r) (1,l,r) 在对应 [ l ,   r ] [l,\, r] [l,r] 的区间上,对 a i + F i − l + 1 a_i + F_{i-l+1} ai+Fil+1其中 i ∈ [ l ,   r ]   F i i\in[l,\, r]\ F_i i[l,r] Fi为斐波那契数列的第 i i i项。其中 F 1 = 1 F 2 = 1 F n = F n − 1 + F n − 2 ( n ≥ 2 ) F_1 = 1\quad F_2=1 \quad F_n = F_{n-1} + F_{n-2} (n\ge2) F1=1F2=1Fn=Fn1+Fn2(n2)
( 2 ,   l ,   r ) (2, \, l,\,r) (2,l,r) [ l ,   r ] [l,\,r] [l,r]区间元素求和,对 1 e 9 + 9 1e9+9 1e9+9取模并输出。 ∑ i = l r a i ( m o d    1 e 9 + 9 ) \sum_{i=l}^ra_i(mod\; 1e9+9) i=lrai(mod1e9+9)

分析

题意为一个较为经典的区间修改和区间查询问题,首先可以想到用线段树去维护,但是维护的难点在于,我们的区间修改并不是加上一个固定的值, 而是对应加上斐波那契数列的不同项。
我们考虑斐波那契数列的通项公式
F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_n = \frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n -(\frac{1-\sqrt{5}}{2})^n ] Fn=5 1[(21+5 )n(215 )n]
其中, 38300801 6 2 ≡ 5 m o d    1 e 9 + 9 ; 383008016^2\equiv5\mod{1e9+9}; 38300801625mod1e9+9;
则, F n = 276601605 ( 69060401 3 n − 30849599 7 n ) m o d    1 e 9 + 9 F_n = 276601605\left(690604013^n-308495997^n\right)\mod 1e9+9 Fn=276601605(690604013n308495997n)mod1e9+9
上式可以通过二次剩余和逆元等一些方法求得,查了几个二次剩余的博客没有看懂,自己直接写了个暴力求了一下。Orz
而我们得到了模意义下的斐波那契数列通项公式之后,我们可以发现,这个数列被拆分成了,两个等比数列的差乘以一个常数的形式。而根据等比数列的求和公式 a 1 1 − q n 1 − q a_1\frac{1-q^n}{1-q} a11q1qn,我们可知对于一个区间的加操作,我们可以通过维护两个不同的首项来直接获得区间加了多少(对应区间的长度是已知的)而求和公式很容易可以看出,满足结合律,所以我们的lazy也就可以直接维护区间对应首项即可,维护首相的时候可以直接对lazy标记进行加和操作。
原问题就转化为了维护两个等比数列的区间求和,区间修改的线段树。
E n d End End

代码

#define int long long
/**
F(n)%mod = 276601605*(691504013^n-308495997^n)%mod   mod=1e9+9
*/

const int q1 = 691504013;
const int q2 = 308495997;
const int mm = 276601605;

struct Node{
    int l, r;
    int sum1, sum2;          //特征值
    int lazy1, lazy2;        //分别维护两个等比数列
}tree[maxn<<2];              //开四倍空间
int num[maxn];               //数值数组
int qn[2][maxn];

void init(){
    ll now = 1;
    qn[0][0] = now;
    rep(i, 1, maxn){
        now = now*q1%mod;
        qn[0][i] = now;
    }
    qn[1][0] = now = 1;
    rep(i, 1, maxn){
        now = now*q2%mod;
        qn[1][i] = now;
    }
}

int qsum(int s, int flag, int n){
    ll ans = s;
    return mm*s%mod*(qn[flag][n]-1)%mod*qn[flag][1]%mod;
}

void build(int i, int l, int r){
    tree[i].l = l;
    tree[i].r = r;
    if(l == r){
        tree[i].sum1 = num[l];
        tree[i].sum2 = 0;
        tree[i].lazy1 = 0;
        tree[i].lazy2 = 0;
        return ;
    }

    int mid = (l+r)>>1;
    build(i<<1, l, mid);
    build(i<<1|1, mid+1, r);    //子树建树后更新特征值
    tree[i].sum1 = tree[i<<1].sum1 + tree[i<<1|1].sum1;
    tree[i].sum2 = tree[i<<1].sum2 + tree[i<<1|1].sum2;
    tree[i].lazy1 = 0;
    tree[i].lazy2 = 0;
    return ;
}

void push_down(int i){ //以lazy表示区间加为例更改特征值
    if(tree[i].lazy1){
        tree[i<<1].sum1 = (tree[i<<1].sum1 + qsum(tree[i].lazy1, 0, (tree[i<<1].r-tree[i<<1].l+1)))%mod;
        tree[i<<1].sum2 = (tree[i<<1].sum2 + qsum(tree[i].lazy2, 1, (tree[i<<1].r-tree[i<<1].l+1)))%mod;
        tree[i<<1|1].sum1 = (tree[i<<1|1].sum1 + qsum(tree[i].lazy1*qn[0][(tree[i<<1].r-tree[i<<1].l+1)]%mod, 0, (tree[i<<1|1].r-tree[i<<1|1].l+1)))%mod;
        tree[i<<1|1].sum2 = (tree[i<<1|1].sum2 + qsum(tree[i].lazy2*qn[1][(tree[i<<1].r-tree[i<<1].l+1)]%mod, 1, (tree[i<<1|1].r-tree[i<<1|1].l+1)))%mod;

        tree[i<<1].lazy1 = (tree[i<<1].lazy1+tree[i].lazy1)%mod;
        tree[i<<1].lazy2 = (tree[i<<1].lazy2+tree[i].lazy2)%mod;
        tree[i<<1|1].lazy1 = (tree[i<<1|1].lazy1+tree[i].lazy1*qn[0][(tree[i<<1].r-tree[i<<1].l+1)]%mod)%mod;
        tree[i<<1|1].lazy2 = (tree[i<<1|1].lazy2+tree[i].lazy2*qn[1][(tree[i<<1].r-tree[i<<1].l+1)]%mod)%mod;
        tree[i].lazy1 = tree[i].lazy2 = 0;
    }
}

void addlr(int i, int l, int r, int s){  // num[l~r] += k
    if(tree[i].l==l && tree[i].r==r){
        tree[i].sum1 = (tree[i].sum1 + qsum(qn[0][s], 0, (tree[i].r-tree[i].l+1)))%mod;
        tree[i].sum2 = (tree[i].sum2 + qsum(qn[1][s], 1, (tree[i].r-tree[i].l+1)))%mod;

        //cout<<"*"<<l<<" "<<r<<" "<<qsum(qn[0][s], 0, (tree[i].r-tree[i].l+1))-qsum(qn[1][s], 1, (tree[i].r-tree[i].l+1))<<endl;

        tree[i].lazy1 = (tree[i].lazy1 + qn[0][s])%mod;
        tree[i].lazy2 = (tree[i].lazy2 + qn[1][s])%mod;
        return ;
    }
    push_down(i);
    int mid = (tree[i].l+tree[i].r)>>1;
    if(mid >= r)
        addlr(i<<1, l, r, s);
    else if(mid < l)
        addlr(i<<1|1, l, r, s);
    else {
        addlr(i<<1, l, mid, s);
        addlr(i<<1|1, mid+1, r, s+(mid-l+1));
    }
    tree[i].sum1 = (tree[i<<1].sum1 + tree[i<<1|1].sum1)%mod;
    tree[i].sum2 = (tree[i<<1].sum2 + tree[i<<1|1].sum2)%mod;
}

int query(int i, int l, int r){  //以sum为例
    if(tree[i].l==l && tree[i].r==r)
        return (tree[i].sum1-tree[i].sum2+mod)%mod;

    push_down(i);

    int mid = (tree[i].l+tree[i].r)>>1;
    if(mid >= r)
        return query(i<<1, l, r);
    else if(mid < l)
        return query(i<<1|1, l, r);
    else
        return (query(i<<1, l, mid) + query(i<<1|1, mid+1, r))%mod;
}

signed main()
{
    init();

    int n, m;
    sldd(n, m);
    rep(i, 1, n+1)
        sld(num[i]);

    build(1, 1, n);

    rep(i, 0, m){
        int x, l, r;
        slddd(x, l, r);
        if(x==1){
            addlr(1, l, r, 1);
        }
        else if(x==2){
            pld(query(1, l, r));
        }
    }
}
/*
4 4
1 2 3 4
1 1 4
2 1 4
1 2 4
2 1 3
*/

总结

这个线段树分别维护了两个 s u m sum sum值和两个 l a z y lazy lazy标记,一开始写的时候写迷糊了,后来理清思路就好很多。
这次专门玩了一手define int long long 表示很开心,主函数改signed就可以了
代码因为开始思路不清晰所以有点丑
中间有多处取模可能会溢出或者忘记取模的部分,WA了两发,应多加注意

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值