数据结构8-差分数组

做到了这个题,传送门
一眼看完就知道是线段树,,,,
但是,,我还是看了题解、、、
线段树会TLE一个点。。。
怎么办呢
注意到,,这道题因为修改和查询是分开的,所以线段树有点浪费。
这道题实际上是一个差分数组

在网上讲差分数组的博文很少,也很难找到。一度以为差分数组是传播于小众的神犇技巧所以一直放着没有去研习。今天做了bzoj1635后发现各路神犇都用差分数组,本蒟却傻傻写了线段树。。。。。
对于序列a{},取a[i]-a[i-1]为其差分数组b[i]的值,可以发现,a[i]=∑bj(1≤j≤i)
如 对于序列 a、b、c、d 其差分数组为 a、 b-a、 c-b、 d-c
(a-0)
有a=a,b=a+(b-a),c=a+(b-a)+(c-b),d=a+(b-a)+(c-b)+(d-c)
那么,如果我们进行区间加减操作,且修改的区间连续不相交,那么,若将(x,y)区间整体加val,我们就可以对差分数组的b[x]加val,b[y+1]减val。此时差分数组所对应的原序列即为(x,y)整体加val后的区间。
如此而已,推一推就明白了。

其实就是记录数组的第 i位与i-1位的差值,这样更新区间的时候只需要对于头尾进行更新就可以了
给出代码

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N = 100000 + 10;
int m, q;
long long a[N], d[N], sum[N];
int main() {
    int n;
    while (scanf("%d", &n) && n) {
        memset(a, 0, sizeof(a));//初始化都为0
        int l, r;
        memset(d, 0, sizeof(d));
        d[1] = a[1];//特殊处理第一个数
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &l, &r);
            d[l] += 1;
            d[r+1] -= 1;
        }
        memset(sum, 0, sizeof(sum));
        for (int i = 1; i <= n; ++i) {
            sum[i] = sum[i-1] + d[i]; //求d[i]前缀和, 也就是修改后的a[i]
        }
        for (int i = 1; i <= n; ++i){
            printf("%lld",sum[i]);
            if(i != n) printf(" ");
        }
        puts("");
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/Liukairui/article/details/81295123