BJOI2018链上二次求和——线段树

本文详细解析了BJOI2018链上二次求和问题的解题思路,通过数学推导和数据结构优化,利用线段树实现区间更新和查询,解决了区间内元素的二次函数变化问题。

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

BJOI2018 链上二次求和

思路

[l,r]的限制可以拆成[1,l-1],[1,r],然后考虑推式子。

\(s_i\)\(a_i\)的前缀和。

\({\rm ans}=\sum_{i=1}^{x}\sum_{j=i}^{n}s_{j}-s_{j-i}\)

\(t_i\)\(s_i\)的前缀和。

\({\rm ans}=\sum_{i=1}^{x}t_{n}-t_{i-1}-t_{n-i}\)

发现如果我们可以区间维护前缀和的前缀和,那么就可以直接区间查询得到答案了。

考虑一次修改对答案造成的影响,发现对于下标为\(i\)的点的贡献为一个\(ai^{2}+bi+c\)形式的多项式,也就是区间加上一个二次函数。

对于区间加上二次函数,我们可以通过简单的线段树打标记来完成,修改一个点时需要用到二次幂和,直接套用公示即可,标记合并直接相加即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.3.31
 * Problem : loj2512
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("loj2512.in","r",stdin);
    freopen("loj2512.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

string proc(){
    ifstream f("/proc/self/status");
    return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=2e5+10;
const int mod=1e9+7;
int n,q,s[maxn];

void mul(int &x,int y){
    x=1ll*x*y%mod;
}
void inc(int &x,int y){
    x+=y;
    if(x>=mod)x-=mod;
    else if(x<0)x+=mod;
}

int qpow(int x,int y=mod-2){
    int ret=1; x%=mod;
    while(y){
        if(y&1)ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return ret;
}

const int inv2=qpow(2);
const int inv6=qpow(6);

#define mid ((l+r)>>1)
#define lc o<<1
#define rc o<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
int sum[maxn<<2],a[maxn<<2],b[maxn<<2],c[maxn<<2];

void build(int o,int l,int r){
    if(l==r)sum[o]=s[l];
    else{
        build(lson),build(rson);
        sum[o]=(sum[lc]+sum[rc])%mod;
    }
}

int S2(int x){return 1ll*x*(x+1)%mod*(2*x+1)%mod*inv6%mod;}
int S1(int x){return 1ll*x*(x+1)%mod*inv2%mod;}

void pushdown(int o,int l,int r){
    if(a[o]){
        inc(sum[lc],1ll*(S2(mid)-S2(l-1))*a[o]%mod),inc(a[lc],a[o]);
        inc(sum[rc],1ll*(S2(r)-S2(mid))*a[o]%mod),inc(a[rc],a[o]);
    }
    if(b[o]){
        inc(sum[lc],1ll*(S1(mid)-S1(l-1))*b[o]%mod),inc(b[lc],b[o]);
        inc(sum[rc],1ll*(S1(r)-S1(mid))*b[o]%mod),inc(b[rc],b[o]);
    }
    if(c[o]){
        inc(sum[lc],1ll*(mid-l+1)*c[o]%mod),inc(c[lc],c[o]);
        inc(sum[rc],1ll*(r-mid)*c[o]%mod),inc(c[rc],c[o]);
    }
    a[o]=b[o]=c[o]=0;
}

void update(int o,int l,int r,int L,int R,int aa,int bb,int cc){
    if(L>R)return;
    if(L<=l && r<=R){
        //debug(l),debug(r)<<endl;
        //printf("%d %d %d\n",aa,bb,cc);
        //debug(sum[o])<<endl;
        inc(sum[o],1ll*(S2(r)-S2(l-1))*aa%mod),inc(a[o],aa);
        inc(sum[o],1ll*(S1(r)-S1(l-1))*bb%mod),inc(b[o],bb);
        inc(sum[o],1ll*(r-l+1)*cc%mod),inc(c[o],cc);
        //debug(sum[o])<<endl;
    }
    else{
        pushdown(o,l,r);
        if(L<=mid)update(lson,L,R,aa,bb,cc);
        if(R>=mid+1)update(rson,L,R,aa,bb,cc);
        sum[o]=(sum[lc]+sum[rc])%mod;
    }
}

int query(int o,int l,int r,int L,int R){
    if(L>R)return 0;
    if(L<=l && r<=R)return sum[o];
    int ret=0;
    pushdown(o,l,r);
    if(L<=mid)inc(ret,query(lson,L,R));
    if(R>=mid+1)inc(ret,query(rson,L,R));
    return ret;
}

int solve(int x){
    return (1ll*query(1,1,n,n,n)*x%mod-query(1,1,n,1,x-1)-query(1,1,n,n-x,n-1))%mod;
}

int main(){
    File();

    read(n),read(q);
    REP(i,1,n)read(s[i]);
    REP(i,1,n)inc(s[i],s[i-1]);
    REP(i,1,n)inc(s[i],s[i-1]);
    build(1,1,n);

    int ty,l,r,x;
    REP(i,1,q){
        read(ty);
        if(ty==1){
            read(l),read(r),read(x);
            if(l>r)swap(l,r);
            update(1,1,n,l,r,1ll*inv2*x%mod,1ll*(3-2*l)*inv2%mod*x%mod,1ll*(2-l)*(1-l)%mod*inv2%mod*x%mod);
            update(1,1,n,r+1,n,0,1ll*(r-l+1)*x%mod,1ll*(2-l-r)*(r-l+1)%mod*inv2%mod*x%mod);
        }
        else{
            read(l),read(r);
            printf("%d\n",((solve(r)-solve(l-1))%mod+mod)%mod);
        }
        /*REP(j,1,n)printf("%d ",query(1,1,n,j,j));
        printf("\n");*/
    }

    return 0;
}

转载于:https://www.cnblogs.com/ylsoi/p/10633632.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值