欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ICPC网络赛 Ryuji doesn't want to study (线段树/树状数组)

程序员文章站 2022-07-13 11:23:44
...

Ryuji doesn’t want to study

题目链接: 计蒜客 - 题库链接

题意

给你N个数,M个询问,有两种操作

  1. 将一个数修改为另一个值
  2. 求区间和 a[l]L+a[l+1](L1)+...+a[r1]2+a[r]

数据范围:N,M<105


思路

前缀和思想

要求的和很奇怪,第一个数要出现N次,第二个数要出现N-1次,这种单调性质,我就想到了前缀和。那么现在我的目标就是如何快速求前缀和的区间和,我本来想在开一个树状数组快速的查询前缀和的前缀和,但是如果要修改一个点x的值,就需要修改 [x,N] 的值仅依靠树状数组显然不行,我就用了线段树区间修改。所以我这是单点查询区间修改。

从图像找规律

ICPC网络赛 Ryuji doesn't want to study (线段树/树状数组)

看图,用两个树状数组实现。


代码

线段树

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(ll i = (ll)j;i <= (ll)k;i ++)
#define per(i,j,k) for(ll i = (ll)j;i >= (ll)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back

typedef double db;
typedef long long ll;
const ll MAXN = (ll)1e5+7;
const ll INF = (ll)0x3f3f3f3f;

#define lson rt<<1
#define rson rt<<1|1

ll N,M;
ll A[MAXN];

ll sum[MAXN<<2];
ll add[MAXN<<2];

void PushUp(ll rt){sum[rt] = sum[lson] + sum[rson]; }

void Build(ll l,ll r,ll rt){
    if (l == r){
        sum[rt] = A[l];
        return ;
    }
    ll m = (l+r)>>1;
    Build(l,m,lson);
    Build(m+1,r,rson);
    PushUp(rt);
}

void PushDown(ll rt,ll ln,ll rn){
    if (add[rt]){
        sum[lson] += add[rt]*ln;
        sum[rson] += add[rt]*rn;

        add[lson] += add[rt];
        add[rson] += add[rt];
        add[rt] = 0;
    }
}

void Update(ll L,ll R,ll C,ll l,ll r,ll rt){
    if (L <= l && r <= R){
        sum[rt] += C*(r+1-l);
        add[rt] += C;
        return ;
    }
    ll m = (l+r)>>1;
    PushDown(rt,m+1-l,r-m);
    if (L <= m) Update(L,R,C,l,m,lson);
    if (R >  m) Update(L,R,C,m+1,r,rson);
    PushUp(rt);
}

ll Query(ll L,ll R,ll l,ll r,ll rt){
    if (L == 0) return 0;
    if (L <= l && r <= R){
        return sum[rt];
    }

    ll m = (l+r)>>1;
    PushDown(rt,m+1-l,r-m);
    ll ans = 0;
    if (L <= m) ans += Query(L,R,l,m,lson);
    if (R >  m) ans += Query(L,R,m+1,r,rson);
    return ans;
}

int main()
{
    scanf("%lld %lld",&N,&M);
    ll sum = 0;
    rep(i,1,N) {
        ll tmp;
        scanf("%lld",&tmp);
        A[i] = A[i-1]+tmp;
    }
    Build(1,N,1);
    while (M --){
        ll a,b,c;
        scanf("%lld %lld %lld",&a,&b,&c);
        if (a == 1) {
            ll sum = 0;
            sum = Query(b,c,1,N,1);
            sum -= Query(b-1,b-1,1,N,1)*(c-b+1);
            printf("%lld\n",sum);
        }else {
            Update(b,N,c-Query(b,b,1,N,1)+Query(b-1,b-1,1,N,1),1,N,1);
        }
    }
}

树状数组

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(ll i = (ll)j;i <= (ll)k;i ++)
#define per(i,j,k) for(ll i = (ll)j;i >= (ll)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back

typedef double db;
typedef long long ll;
const ll MAXN = (ll)1e6+7;
const ll INF = (ll)0x3f3f3f3f;

ll A[MAXN],B[MAXN],N,M;
ll lowbit(ll x) {return x&(-x); }

void update(ll L,ll val) {
    while (L <= N) {
        A[L] += val;
        L += lowbit(L);
    }
}

ll getsum(ll L) {
    ll sum = 0;
    while (L > 0) {
        sum += A[L];
        L -= lowbit(L);
    }
    return sum;
}

void update2(ll L,ll val) {
    while (L <= N) {
        B[L] += val;
        L += lowbit(L);
    }
}

ll getsum2(ll L) {
    ll sum = 0;
    while (L > 0) {
        sum += B[L];
        L -= lowbit(L);
    }
    return sum;
}

int main()
{
    scanf("%lld %lld",&N,&M);
    rep(i,1,N) {
        ll tmp;
        scanf("%lld",&tmp);
        update(i,tmp);
        update2(i,tmp*(N-i+1));
    }
    rep(i,1,M) {
        ll op,x,y;
        scanf("%lld %lld %lld",&op,&x,&y);
        if (op == 1) {
            ll ans1 = getsum2(y) - getsum2(x-1);
            ll ans2 = getsum(y) - getsum(x-1);
            printf("%lld\n",ans1-ans2*(N-y));
        }else {
            ll dif = getsum(x) - getsum(x-1);
            update(x,y-dif);
            update2(x,(y-dif)*(N-x+1));
        }
    }
}