Codeforces 316 E3 线段树

题目链接


题意

给定一个长度为\(n\)序列\(a\),需要支持如下操作

  • 单点修改

  • 对于区间\([l..r]\),求\(\sum_{i=0}^{r-l}f_i*a_{l+i}\),模\(10^9\),其中\(f\)是斐波那契数列,\(f_0=f_1=1\)

  • 区间加

做法

建线段树,对于一个节点\([l..r]\),记

\[s_k=\sum_{i=0}^{r-l}f_{i+k}*a_{l+i}\]

存在以下性质
\[ \begin{align} &s_{k-2}+s_{k-1}\\ =&\sum_{i=0}^{r-l}(f_{i+k-2}+f_{i+k-1})*a_{l+i}\\ =&\sum_{i=0}^{r-l}f_{i+k}*a_{l+i}\\ =&s_k \end{align} \]

只需要维护\(s_0\)\(s_1\),预处理斐波那契数列作为系数,可以\(O(1)\)求出\(s_k\)

合并两个区间的时候,需要求出第二个区间的\(s_{len},s_{len+1}\),其中\(len\)是第一个区间的长度

区间加,可以利用\[\sum_{i=0}^nf_i=f_{n+2}-1\]快速修改

#include<cstdio>
#include<algorithm>
#include<ctype.h>
#include<string.h>
#include<math.h>

using namespace std;
#define ll long long

inline char read() {
    static const int IN_LEN = 1000000;
    static char buf[IN_LEN], *s, *t;
    return (s == t ? t = (s = buf) + fread(buf, 1, IN_LEN, stdin), (s == t ? -1 : *s++) : *s++);
}
template<class T>
inline void read(T &x) {
    static bool iosig;
    static char c;
    for (iosig = false, c = read(); !isdigit(c); c = read()) {
        if (c == '-') iosig = true;
        if (c == -1) return;
    }
    for (x = 0; isdigit(c); c = read()) x = ((x + (x << 2)) << 1) + (c ^ '0');
    if (iosig) x = -x;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN], *ooh = obuf;
inline void print(char c) {
    if (ooh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), ooh = obuf;
    *ooh++ = c;
}
template<class T>
inline void print(T x) {
    static int buf[30], cnt;
    if (x == 0) print('0');
    else {
        if (x < 0) print('-'), x = -x;
        for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
        while (cnt) print((char)buf[cnt--]);
    }
}
inline void flush() { fwrite(obuf, 1, ooh - obuf, stdout); }
const int N = 200005, P = 1000000000;
int n, m, f[N], a[N], lazy[N<<2];
struct st{
    int a, b, len;
    inline st(){}
    inline st(int a_, int b_, int len_){ a=a_, b=b_, len=len_;}
    inline st operator *(const st &rhs)const{
        return st((a+(ll)rhs.a*f[len-1]+(ll)rhs.b*f[len])%P, (b+(ll)rhs.a*f[len]+(ll)rhs.b*f[len+1])%P, len+rhs.len);
    }
}s[N<<2];
void build(int l, int r, int t){
    if(l==r) return (void)(s[t].a=s[t].b=a[l], s[t].len=1);
    int mid=l+r>>1, k=t<<1;
    build(l, mid, k), build(mid+1, r, k|1);
    s[t]=s[k]*s[k|1];
}
inline void add(int t, int x){
    (lazy[t]+=x)%=P;
    s[t].a=(s[t].a+(ll)(f[s[t].len+2]-1)*x)%P;
    s[t].b=(s[t].b+(ll)(f[s[t].len+3]-2)*x)%P;
}
void modify(int l, int r, int t, int x, int y){
    if(l==r) return (void)(s[t].a=s[t].b=y);
    int mid=l+r>>1, k=t<<1;
    if(lazy[t]) add(k, lazy[t]), add(k|1, lazy[t]), lazy[t]=0;
    if(x<=mid) modify(l, mid, k, x, y); else modify(mid+1, r, k|1, x, y);
    s[t]=s[k]*s[k|1];
}
st query(int l, int r, int t, int L, int R){
    if(L<=l && r<=R) return s[t];
    int mid=l+r>>1, k=t<<1;
    if(lazy[t]) add(k, lazy[t]), add(k|1, lazy[t]), lazy[t]=0;
    if(R<=mid) return query(l, mid, k, L, R);
    if(L>mid) return query(mid+1, r, k|1, L, R);
    return query(l, mid, k, L, R)*query(mid+1, r, k|1, L, R);
}
void change(int l, int r, int t, int L, int R, int x){
    if(L<=l && r<=R) return add(t, x);
    int mid=l+r>>1, k=t<<1;
    if(lazy[t]) add(k, lazy[t]), add(k|1, lazy[t]), lazy[t]=0;
    if(L<=mid) change(l, mid, k, L, R, x);
    if(R>mid) change(mid+1, r, k|1, L, R, x);
    s[t]=s[k]*s[k|1];
}
int main() {
    read(n), read(m);
    f[1]=f[2]=1;
    for(int i=3; i<=n+3; ++i) f[i]=(f[i-1]+f[i-2])%P;
    for(int i=1; i<=n; ++i) read(a[i]);
    build(1, n, 1);
    while(m--){
        static int opt, l, r, x;
        read(opt), read(l), read(r);
        if(opt==1) modify(1, n, 1, l, r);
        else if(opt==2) print(query(1, n, 1, l, r).a), print('\n');
        else read(x), change(1, n, 1, l, r, x);
    }
    return flush(), 0;
}

转载于:https://www.cnblogs.com/CMXRYNP/p/9651026.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值