进阶 (*1900) 南极洲大蜗牛之痛 题解

周子清| 23/04/17 20:08| 197| 1| 0| 0| 3181
学习题解

测试一下博客

集训队出的题目,网上没有题解,于是写一个自己的思路

想让博客变得和涛涛一样


题意:

思路:

因为询问求和,所以肯定要让ai*ki维护成一个整体才能相加,不然如果分开,区间取到ai*ki且i是维护一个区间的时候得到的值就有误了。因为此时的值应该是al*kl+a(l+1)*k(l+2)+.......+ar*kr而不是现在维护的(al+a(l+1)+...ar)*(kl+k(l+1)+...kr),因为分开维护会导致把a或k看成整体,而不是a*k。

所以维护ai*ki,单独在结构体定义一个sum,我们发现,当a区间+w的时候,sum其实变化的就是+(k*w),k区间加的时候同理,所以只要在pushdown里额外加一个对sum的传递就好了,详细见代码。

代码

半年前的码风,看个思路就行啦。

#include <bits/stdc++.h>
//#define int long long
#define CIO std::ios::sync_with_stdio(false)
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define nep(i, r, l) for (int i = r; i >= l; i--)
#define pii pair<int,int>
using namespace std;
const int N=2e5+5;
struct tree{
    int a,k,sum;
}tr[4*N];
int num[N],laza[4*N],lazk[4*N];
void builda(int p,int l,int r){
    if (l==r){
        tr[p].a=num[l];
        return;
    }
    int mid=l+r>>1;
    builda(2*p,l,mid);
    builda(2*p+1,mid+1,r);
    tr[p].a=tr[2*p].a+tr[2*p+1].a;
}
void buildk(int p,int l,int r){
    if (l==r){
        tr[p].k=num[l];
        tr[p].sum=tr[p].k*tr[p].a;
        return;
    }
    int mid=l+r>>1;
    buildk(2*p,l,mid);
    buildk(2*p+1,mid+1,r);
    tr[p].k=tr[2*p].k+tr[2*p+1].k;
    tr[p].sum=tr[2*p].sum+tr[2*p+1].sum;
}
void pushdown(int p,int l,int r){
    int mid=l+r>>1;
    laza[2*p]+=laza[p];
    laza[2*p+1]+=laza[p];
    tr[2*p].sum+=laza[p]*tr[2*p].k;
    tr[2*p+1].sum+=laza[p]*tr[2*p+1].k;
    tr[2*p].a+=laza[p]*(mid-l+1);
    tr[2*p+1].a+=laza[p]*(r-mid);
    lazk[2*p]+=lazk[p];
    lazk[2*p+1]+=lazk[p];
    tr[2*p].sum+=lazk[p]*tr[2*p].a;
    tr[2*p+1].sum+=lazk[p]*tr[2*p+1].a;
    tr[2*p].k+=lazk[p]*(mid-l+1);
    tr[2*p+1].k+=lazk[p]*(r-mid);
    lazk[p]=0;
    laza[p]=0;
}
int query(int p,int l,int r,int x,int y){
    if (x<=l&&r<=y){
        return tr[p].sum;
    }
    pushdown(p,l,r);
    int mid=l+r>>1,ans=0;
    if (x<=mid)ans+=query(2*p,l,mid,x,y);
    if (mid<y)ans+=query(2*p+1,mid+1,r,x,y);
    return ans;
}
void updatea(int p,int l,int r,int x,int y,int w){
    if (x<=l&&r<=y){
        tr[p].sum+=w*tr[p].k;
        tr[p].a+=w*(r-l+1);
        laza[p]+=w;
        return;
    }
    pushdown(p,l,r);
    int mid=l+r>>1;
    if (x<=mid)updatea(2*p,l,mid,x,y,w);
    if (mid<y)updatea(2*p+1,mid+1,r,x,y,w);
    tr[p].a=tr[2*p].a+tr[2*p+1].a;
    tr[p].sum=tr[2*p].sum+tr[2*p+1].sum;
}
void updatek(int p,int l,int r,int x,int y,int w){
    if (x<=l&&r<=y){
        tr[p].sum+=w*tr[p].a;
        tr[p].k+=w*(r-l+1);
        lazk[p]+=w;
        return;
    }
    pushdown(p,l,r);
    int mid=l+r>>1;
    if (x<=mid)updatek(2*p,l,mid,x,y,w);
    if (mid<y)updatek(2*p+1,mid+1,r,x,y,w);
    tr[p].k=tr[2*p].k+tr[2*p+1].k;
    tr[p].sum=tr[2*p].sum+tr[2*p+1].sum;
}
void work(){
    int n,q;
    cin>>n>>q;
    rep(i,1,n){
        cin>>num[i];
    }
    builda(1,1,n);
    rep(i,1,n){
        cin>>num[i];
    }
    buildk(1,1,n);
    int op,l,r,w;
    rep(i,1,q){
        cin>>op;
        if (op==1){
            cin>>l>>r>>w;
            updatea(1,1,n,l,r,w);
        }
        else if (op==2){
            cin>>l>>r>>w;
            updatek(1,1,n,l,r,w);
        }
        else{
            cin>>l>>r;
            cout<<query(1,1,n,l,r)<<endl;
        }
    }
}
signed main(){
    CIO;
    work();
    return 0;
}

总结:

一道好的线段树板子练习题


哭哭,没备份误删了一次,咕咕...

本文作者: 周子清
本文链接: http://127.0.0.1/blog/private/202132110154/p/6/view
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主: 如果您觉得文章对您有帮助,可以点击右侧【推荐】一下。
1
0

评论列表


暂无评论