Poj P3468 A Simple Problem with Integers___分块/树状数组/线段树

题目大意:

给出N个数AiQ,每次操作有2种方式,
①给出区间[l,r]以及一个数x,表示给[l,r]中的每个Ai都加上一个x
②给出[l,r],回答区间[l,r]的数的总和

1NQ105
108Ai108
104x104

分析:

这题其实有多种解决方法,
①分块:
我们将序列A分成N块,
每一块的长度都是N,除了最后一块不确定。
这个显然。
我们设
Add[i]i
Sum[i]iAdd[i]
Orz[j]jjiAdd[i]
显然Orz的初值就是序列A
Sum[i]
而后面的插入x的操作,
就是在区间[l,r]将他们之间能被完全覆盖的块的Add[]加上x
lr

[ll]中的所有Orz[]加上x
[rr]中的所有Orz[]加上x
此时对应的Sum[i]
然后注意一下当l,r在同一块的时候,
直接扫一遍Orz[]即可
查询的时候也很简单,
[l,r]之间的全部块,直接Add[]Sum[]计入答案即可
而对于头尾的块,扫一遍Orz[],且累加所属块的Orz[]就可以了
同样要注意一下l,r在同一个块的情况
总的思想就是“大段维护、局部朴素”
时间复杂度:O((N+Q)N)

②树状数组:
在利用树状数组完成这题之前,
要先去解决用树状数组+差分实现的“区间增加+单点查询”到“单点增加+区间查询”的做法
题目链接:
https://www.luogu.org/problemnew/show/P3368

好了,我们设
d[]
ASum[]
那么题目显然对于一个[l,r]
显然求的就是
Sum[r]Sum[l1]+i=lrj=1id[j]
Sum[r]Sum[l1]是已知的,
我们考虑一下i=lrj=1id[j]
显然可以对于一个
i=1rj=1id[j](i1l)
可以转化成i=1r(ri+1)d[i]
进一步转化就是
r+1)i=1rd[i]i=1rid[i]
那么我们就可以利用两个树状数组S1,S2分别维护d[i]id[i]的前缀和
每次的区间[l,r]的修改,都可以变成对于差分数组d[l]d[r+1]的修改,
d[l]xd[r+1]x
那么我们所维护的前缀和中,
只需要在树状数组中稍微修改,即
S1lxr+1x
S2llxr+1(r+1)x

然后我们每次查询[l,r]
我们可以通过树状数组求出
i=1rd[i]
i=1rid[i]
那么
(r+1)i=1rd[i]i=1rid[i]
就是
i=1rj=1id[j]

同理可以求出
i=1lj=1id[j]

他们的差就是
i=lrj=1id[j]
加上一开始的Sum[r]Sum[l1]
就可以实现查询了

时间复杂度:O((N+Q)logN)

③线段树:
这种方法,就很容易实现跟理解了,
自行百度吧,
注意一下这题要打Lazy标记即可

代码:

①分块:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define N 200005

using namespace std;

typedef long long LL;

struct Node {
    int l, r;
    LL Sum;
}Cp[505];
LL Belong[N], Add[N], Op[N], num;
int ans, n, m, tot;

void Insert(int l, int r) {
    int x = Belong[l], y = Belong[r];
    if (x == y) {
        for (int i = l; i <= r; i++) Op[i] += num;
        Cp[x].Sum += (r - l + 1) * num;
        return;
    }
    for (int i = x + 1; i <= y - 1; i++) Add[i] += num;
    for (int i = l; i <= Cp[x].r; i++) Op[i] += num;
    for (int i = Cp[y].l; i <= r; i++) Op[i] += num;
    Cp[x].Sum += (Cp[x].r - l + 1) * num;
    Cp[y].Sum += (r - Cp[y].l + 1) * num; 
}

LL Get_Sum(int l, int r) {
    int x =  Belong[l], y = Belong[r];
    LL Rp = 0;
    if (x == y) { 
        for (int i = l; i <= r; i++) Rp += Op[i];
        Rp += Add[x] * (r - l + 1);
    } else {
        for (int i = x + 1; i <= y - 1; i++) 
             Rp += Cp[i].Sum + Add[i] * (Cp[i].r - Cp[i].l + 1);
        for (int i = l; i <= Cp[x].r; i++) Rp += Op[i];
        for (int i = Cp[y].l; i <= r; i++) Rp += Op[i];
        Rp += Add[x] * (Cp[x].r - l + 1);
        Rp += Add[y] * (r - Cp[y].l + 1);
    }
    return Rp;
}

int main() {
    scanf("%d %d", &n, &m);
    int Rp = sqrt(n);
    tot = 0;
    for (int i = 1; i <= n; i++) {
         scanf("%lld", &num);
         if (i <= tot * Rp) Cp[tot].r = i; 
                       else tot++, Cp[tot].l = i;
         Cp[tot].Sum += num;   
         Belong[i] = tot;
         Op[i] = num;
    }
    Cp[tot].r = n;
    while (m--) {
         char str[2];
         int l, r;
         scanf("%s %d %d", str, &l, &r);
         if (str[0] == 'C') {
             scanf("%lld", &num);
             Insert(l, r);
         } else printf("%lld\n", Get_Sum(l, r));
    }
    return 0;
}

②树状数组:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 100005

using namespace std;

typedef long long LL;

LL c[2][N], sum[N];
int n, m;

int lowbit(int x) {
    return x & (-x);
}

void Insert(int k, int x, int num) {
    while (x <= n) {
           c[k][x] += num;
           x += lowbit(x);
    }
}

LL Getsum(int k, int x){
    LL rp = 0;
    while (x) {
        rp += c[k][x];
        x -= lowbit(x);
    }
    return rp;
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
         int x;
         scanf("%d", &x);
         sum[i] = sum[i - 1] + x;
    }
    while (m--) {
        char str[2];
        int l, r, x;
        scanf("%s %d %d", str, &l, &r);
        if (str[0] == 'C'){
            scanf("%d", &x);
            Insert(0, l, x);
            Insert(1, l, l * x);
            Insert(0, r + 1, - x);
            Insert(1, r + 1, - (r + 1) * x);
        } else {
            LL ans = (sum[r] - sum[l - 1]) + 
                     ((r + 1) * Getsum(0, r) - Getsum(1, r)) -
                     (l * Getsum(0, l - 1) - Getsum(1, l - 1));
            printf("%lld\n", ans);
        }
    }
    return 0;
}

③线段树:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 500005

using namespace std;

typedef long long LL;

LL Tree[N], Lazy[N], num, ans;
int n, m;

void Insert(int W, int l, int r, int x, int y) {
    if (l == x && r == y) {
        Tree[W] += (r - l + 1) * num;
        Lazy[W] += num;
        return;
    }
    int mid = (l + r) >> 1;
    Tree[W * 2] += (mid - l + 1) * Lazy[W];
    Tree[W * 2 + 1] += (r - mid) * Lazy[W];
    Lazy[W * 2] += Lazy[W];
    Lazy[W * 2 + 1] += Lazy[W];
    Lazy[W] = 0;
    if (y <= mid) Insert(W * 2, l, mid, x, y);
             else if (x > mid) Insert(W * 2 + 1, mid + 1, r, x, y);
                          else {
                               Insert(W * 2, l, mid, x, mid);
                               Insert(W * 2 + 1, mid + 1, r, mid + 1, y);
                          }
    Tree[W] = Tree[W * 2] + Tree[W * 2 + 1];
}

void Count_Sum(int W, int l, int r, int x, int y) {
    if (l == x && r == y) {
        ans += Tree[W];
        return;
    }
    int mid = (l + r) >> 1;
    Tree[W * 2] += (mid - l + 1) * Lazy[W];
    Tree[W * 2 + 1] += (r - mid) * Lazy[W];
    Lazy[W * 2] += Lazy[W];
    Lazy[W * 2 + 1] += Lazy[W];
    Lazy[W] = 0;
    if (y <= mid) Count_Sum(W * 2, l, mid, x, y);
             else if (x > mid) Count_Sum(W * 2 + 1, mid + 1, r, x, y);
                          else {
                               Count_Sum(W * 2, l, mid, x, mid);
                               Count_Sum(W * 2 + 1, mid + 1, r, mid + 1, y);
                          }
}


int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
         scanf("%lld", &num);
         Insert(1, 1, n, i, i);
    }
    while (m--) {
        char str[2];
        int l, r;
        scanf("%s %d %d", str, &l, &r);
        if (str[0] == 'C') {
            scanf("%lld", &num);
            Insert(1, 1, n, l, r);
        } else if (str[0] == 'Q') {
            ans = 0;
            Count_Sum(1, 1, n, l, r);
            printf("%lld\n", ans);
        }
    }
    return 0;
阅读更多
版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/80685125
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭