【CodeForces 316E3】Summer Homework(线段树 + 数论)

本文介绍了一道名为SummerHomework的问题解决思路,该问题涉及数列操作和查询,通过线段树结合斐波那契数列进行高效求解。文章详细解析了两种合并节点的方法,并附带了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

【CodeForces 316E3】Summer Homework


题目大意

给定一个数列 a1 a 1 , a2 a 2 ··· an a n ,支持三种操作:
1 x v 表示 axv a x ← v
2 l r 表示询问 rli=0fiai+l ∑ i = 0 r − l f i a i + l (f0=1,f1=1,fi=fi2+fi1) ( f 0 = 1 , f 1 = 1 , f i = f i − 2 + f i − 1 )
3 l r v 表示对于所有的 lxr l ≤ x ≤ r axax+v a x ← a x + v


题解

果断使用线段树
si=ij=0fi s i = ∑ j = 0 i f i
point p o i n t 为当前节点
lftson l f t s o n 为当前节点的左儿子
rhtson r h t s o n 为当前节点的右儿子
每一个节点维护 len= l e n = 区 间 长 度 sum0=rli=0fiai+l s u m 0 = ∑ i = 0 r − l f i a i + l sum1=rli=0fi+1ai+l s u m 1 = ∑ i = 0 r − l f i + 1 a i + l

方法一:使用矩阵快速幂
问题一:如何合并两个节点?
(sum0[point],sum1[point])=(sum0[lftson],sum1[lftson])+(sum0[rhtson],sum1[rhtson])×(0111)len[lftson] ( s u m 0 [ p o i n t ] , s u m 1 [ p o i n t ] ) = ( s u m 0 [ l f t s o n ] , s u m 1 [ l f t s o n ] ) + ( s u m 0 [ r h t s o n ] , s u m 1 [ r h t s o n ] ) × ( 0 1 1 1 ) l e n [ l f t s o n ]

问题二:区间加法对节点的贡献是什么?
len[point]I=0fi=v×len[point] ∑ I = 0 l e n [ p o i n t ] f i = v × l e n [ p o i n t ]

方法二:不使用矩阵
问题:如何合并两个节点?
sum0[point]=sum0[lftson]+sum0[rhtson]×flen[lftson]2+sum1[rhtson]×flen[lftson]1 s u m 0 [ p o i n t ] = s u m 0 [ l f t s o n ] + s u m 0 [ r h t s o n ] × f l e n [ l f t s o n ] − 2 + s u m 1 [ r h t s o n ] × f l e n [ l f t s o n ] − 1
sum1[point]=sum1[lftson]+sum0[rhtson]×flen[lftson]1+sum1[rhtson]×flen[lftson] s u m 1 [ p o i n t ] = s u m 1 [ l f t s o n ] + s u m 0 [ r h t s o n ] × f l e n [ l f t s o n ] − 1 + s u m 1 [ r h t s o n ] × f l e n [ l f t s o n ]

总结:方法二只是形式上省掉了矩阵乘法,本质没有改变,但是代码变得更加简洁常数更小,跑得更快。

再转一个很不错的题解:Ice Princess 的博客


代码(方法二)

#include <cstdio>
#define mxn 200000
#define mod 1000000000
#define ls (p<<1)
#define rs (ls|1)
#define md (l+r>>1)
int n,m,f[mxn|1],s[mxn|1],a[mxn<<2|2];
inline int fibonacci(int p);
inline int plusmod(int x,int y);
inline int plusmod(int x,int y,int z);
inline int mulmod(int x,int y);
inline int minusmid(int x,int y);
struct node {
    int len,sum0,sum1;
    node() {}
    node(int l,int s0,int s1) {
        len=l,sum0=s0,sum1=s1;
    }
    node operator+(const node &o) const {
        node x(len+o.len,0,0);
        x.sum0=plusmod(sum0,mulmod(o.sum0,fibonacci(len-2)),mulmod(o.sum1,fibonacci(len-1)));
        x.sum1=plusmod(sum1,mulmod(o.sum0,fibonacci(len-1)),mulmod(o.sum1,fibonacci(len)));
        return x;
    }
} t[mxn<<2|2];
inline int plusmod(int x,int y) {
    x+=y,x-=(x>=mod?mod:0);
    return x;
}
inline int plusmod(int x,int y,int z) {
    return plusmod(plusmod(x,y),z);
}
inline int minusmod(int x,int y) {
    x-=y,x+=(x<0?mod:0);
    return x;
}
inline int mulmod(int x,int y) {
    return 1ll*x*y%mod;
}
void prework() {
    f[0]=f[1]=1;
    for(int i=2;i<=mxn;i++)
        f[i]=plusmod(f[i-1],f[i-2]);
    s[0]=1;
    for(int i=1;i<=mxn;i++)
        s[i]=plusmod(s[i-1],f[i]);
}
inline int fibonacci(int p) {
    return p<0?0:f[p];
}
void build(int p,int l,int r) {
    if(l==r) {
        scanf("%d",&t[p].sum0);
        t[p].len=1,t[p].sum1=t[p].sum0;
        return;
    }
    build(ls,l,md);
    build(rs,md+1,r);
    t[p]=t[ls]+t[rs];
}
void update(int p,int l,int r,int x) {
    int len=r-l+1;
    t[p].sum0=plusmod(t[p].sum0,mulmod(x,s[len-1]));
    t[p].sum1=plusmod(t[p].sum1,minusmod(mulmod(x,s[len]),x));
}
void push_down(int p,int l,int r) {
    if(!a[p])   return;
    if(l-r) {
        a[ls]=plusmod(a[ls],a[p]);
        a[rs]=plusmod(a[rs],a[p]);
        update(ls,l,md,a[p]);
        update(rs,md+1,r,a[p]);
    }
    a[p]=0;
}
void change(int p,int l,int r,int x,int y) {
    if(l==r) {
        t[p]=node(1,y,y);
        return;
    }
    push_down(p,l,r);
    if(x<=md)   change(ls,l,md,x,y);
    else    change(rs,md+1,r,x,y);
    t[p]=t[ls]+t[rs];
}
void modify(int p,int l,int r,int lx,int rx,int x) {
    if(l==lx&&r==rx) {
        a[p]=plusmod(a[p],x);
        update(p,l,r,x);
        return;
    }
    push_down(p,l,r);
    if(rx<=md)  modify(ls,l,md,lx,rx,x);
    else if(lx>md)  modify(rs,md+1,r,lx,rx,x);
    else    modify(ls,l,md,lx,md,x),modify(rs,md+1,r,md+1,rx,x);
    t[p]=t[ls]+t[rs];
}
node query(int p,int l,int r,int lx,int rx) {
    if(l==lx&&r==rx)    return t[p];
    push_down(p,l,r);
    if(rx<=md)  return query(ls,l,md,lx,rx);
    if(lx>md)   return query(rs,md+1,r,lx,rx);
    return query(ls,l,md,lx,md)+query(rs,md+1,r,md+1,rx);
}
int main() {
    prework();
    scanf("%d%d",&n,&m);
    build(1,1,n);
    int op,x,y,z;
    for(int i=1;i<=m;i++) {
        scanf("%d%d%d",&op,&x,&y);
        switch(op) {
        case 1:
            change(1,1,n,x,y);
            break;
        case 2:
            printf("%d\n",query(1,1,n,x,y).sum0);
            break;
        case 3:
            scanf("%d",&z);
            modify(1,1,n,x,y,z);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值