差分标记讲解

引论

       维护区间信息的数据结构有很多,像线段树、树状数组等;然而线段树之类的数据结构往往要写上一段板子(尽管不是太长),但在算法竞赛中却很有可能导致我们与别人慢上那么几分钟,所以我们需要准备一种更简单实用的数据结构,前缀和、差分标记就是这样的数据结构。

正文

适用题型

      我们每次对一个区间进行加或者减,最后我们求一下每个点的值

解决方法

    差分标记其实与前缀和有着密切的关系,我们开始时有一个初始化为0的sum数组,每次我们输入一个区间a, b和要增加的值d,我们使得sum[a] + d, sum[b] - d,最后我们设一个变量res为0,for 循环遍历sum数组每次使res加上sum[i],得到的结果就是这个结点经过修改后最终得到的值

例题

例题1、Color the ball

 

题目来源:HDU 2006-12 Programming Contest

题目描述:N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?

分析:差分标记的板子题

参考代码

#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

#define N 110000

int sum[N];

int main() {
    int n;
    while (scanf("%d", &n) != EOF && n) {
        memset(sum, 0, sizeof sum);
        for (int i = 0; i < n; ++i) {
            int a, b; scanf("%d%d", &a, &b);
            ++sum[a]; --sum[b + 1];
        }
        int res = 0;
        for (int i = 1; i < n; ++i) {
            res += sum[i];
            printf("%d ", res);
        }
        res += sum[n];
        printf("%d\n", res);
    }
    return 0;
}

例题2、区间  

题目来源:牛客小白月赛5

题目描述:

有一个n个元素的数组a,而他要对a[L]-a[R]进行M次操作:

        操作一:将a[L]-a[R]内的元素都加上P

        操作二:将a[L]-a[R]内的元素都减去P

最后询问a[l]-a[r]内的元素之和

分析:这个题涉及到了区间信息的修改,很容易让人想到线段树或者树状数组这类能够动态的维护区间信息的数据结构,实际上这只是一个误导。如果我们认真读题的话我们会发现我们仅仅需要查询一次(加粗体的部分),这时采用BIT或者线段树这种难写(相比于前缀和)的数据结构显然是不合适的。一种简单的方法是用差分标记记录哪些区间进行了修改,然后直接用for循环扫一遍就可以了

#include <stdio.h>
#include <string.h>

#define N 1100000

typedef long long ll;

int a[N], b[N];

int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
        }
        memset(b, 0, sizeof(b));
        while (m--) {
            int q, l, r, p;
            scanf("%d%d%d%d", &q, &l, &r, &p);
            if (q == 1) {
                b[l] -= p; b[r + 1] += p;
            }
            else {
                b[l] += p; b[r + 1] -= p;
            }
        }
        ll res = 0, tmp = 0;
        int l, r; scanf("%d%d", &l, &r);
        for (int i = 1; i <= n; ++i) {
            tmp += b[i];
            if (l <= i && i <= r) res = res + tmp + a[i];
        }
        printf("%lld\n", res);
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值