前缀和差分

一维前缀和

cpp 复制代码
int n, m, l, r;
int a[N], s[N]; 
cin >> n;
for (int i = 1; i <= n; i++) { 
    cin >> a[i];
    s[i] = s[i-1] + a[i]; // 构建前缀和
}
cin >> m;
while (m--) {
    cin >> l >> r;
    cout << s[r] - s[l-1] << endl; // O(1) 查询区间和
}

注意,1.N值一定要够大。或者用vector<int> a(N,0)初始化。

2.数组从1开始遍历。

一维差分

cpp 复制代码
int n, q, l, r, x;
int a[N], b[N]; // b为差分数组
cin >> n >> q;
for (int i = 1; i <= n; i++) {
    cin >> a[i];
    b[i] = a[i] - a[i-1]; // 构建差分
}
while (q--) {
    cin >> l >> r >> x;
    b[l] += x; b[r+1] -= x; // 区间修改
}
for (int i = 1; i <= n; i++) {
    a[i] = a[i-1] + b[i]; // 还原数组
    cout << a[i] << " ";
}

1.差分构建还原,熟练。

二维前缀和

cpp 复制代码
int n, m, q, x1, y1, x2, y2;
int a[N][N], s[N][N]; 
// 构建二维前缀和
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
// 查询子矩阵和
int sum = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];

二维差分

cpp 复制代码
int n, m, x1, y1, x2, y2;
int b[N][N]; // 差分矩阵
// 对子矩阵 (x1,y1) 到 (x2,y2) 进行 +c 操作
void add(int x1, int y1, int x2, int y2, int c) {
    b[x1][y1] += c;
    b[x1][y2+1] -= c;
    b[x2+1][y1] -= c;
    b[x2+1][y2+1] += c;
}
// 最后求二维前缀和还原原矩阵
for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      a[i][j]=d[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
      cout<<a[i][j];
    }
    cout<<'\n';
  }

重新排序

问题描述

给定一个数组 𝐴

和一些查询

𝐿

𝑖

,

𝑅

𝑖

, 求数组中第

𝐿

𝑖

至第

𝑅

𝑖

个元素之和。

小蓝觉得这个问题很无聊, 于是他想重新排列一下数组, 使得最终每个查 询结果的和尽可能地大。小蓝想知道相比原数组, 所有查询结果的总和最多可 以增加多少?

输入格式

输入第一行包含一个整数

𝑛

第二行包含

𝑛

个整数

𝐴

1

,

𝐴

2

,

,

𝐴

𝑛

, 相邻两个整数之间用一个空格分隔。

第三行包含一个整数

𝑚

表示查询的数目。

接下来

𝑚 行, 每行包含两个整数 𝐿𝑖、𝑅𝑖, 相邻两个整数之间用一个空格分 隔。

输出格式

输出一行包含一个整数表示答案。

样例输入

5

1 2 3 4 5

2

1 3

2 5

样例输出

4

样例说明

原来的和为 6+14=20, 重新排列为 (1,4,5,2,3)后和为 10+14=24, 增 加了 4。

思路:问题转化:要最大化所有查询结果的总和,等价于让"值大的数字"尽量出现在"被查询次数多的位置"上。因为无论数组顺序如何,每个位置被查询的总次数是固定的。

  1. 统计次数(差分):我们需要一个高效的方法,计算出数组的每个位置被查询了多少次。如果对每个查询区间 [Li, Ri] 都遍历一遍加1,复杂度是 O(n * m),在 n,m ≤ 1e5 的规模下会严重超时。

◦ 解法:使用差分数组。diff[l] += 1, diff[r+1] -= 1。处理完所有查询后,对 diff 求一次前缀和,就得到了每个位置被查询的次数 cnt[i]。这个过程将区间更新优化到了 O(n + m) 的时间复杂度-

11

16

  1. 重新排序(贪心 + 排序不等式):既然想让"大数"配"大次数",那很简单:把原始数组 A 和被查询次数的数组 cnt都按非递增顺序排序,然后将对应位置相乘并求和,就能得到重新排序后的最大可能总和-

5

17

。这背后用到了"排序不等式"的原理。

  1. 计算答案:最终的答案 = 最大可能总和 - 原数组总和。
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;

int n, m;
int a[N], cnt[N]; // a存储原数组,cnt存储每个位置被查询的次数

int main() {
    // 1. 输入数据
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    
    // 2. 差分统计查询次数 (区间修改)
    cin >> m;
    while (m--) {
        int l, r;
        cin >> l >> r;
        cnt[l]++;   // 差分标记:区间开始+1
        cnt[r+1]--; // 差分标记:区间结束后-1
    }
    
    // 3. 前缀和还原真实次数
    for (int i = 1; i <= n; ++i) {
        cnt[i] += cnt[i-1];
    }
    
    // 4. 计算原数组的查询总和
    LL original_sum = 0;
    for (int i = 1; i <= n; ++i) {
        original_sum += (LL)a[i] * cnt[i];
    }
    
    // 5. 排序:让大的数字去对应大的查询次数
    sort(a + 1, a + n + 1);       // 升序
    sort(cnt + 1, cnt + n + 1);   // 升序
    
    // 6. 计算重新排序后的最大可能总和
    LL max_sum = 0;
    for (int i = 1; i <= n; ++i) {
        max_sum += (LL)a[i] * cnt[i];
    }
    
    // 7. 输出最多能增加的值
    cout << max_sum - original_sum << endl;
    
    return 0;
}
相关推荐
生成论实验室3 小时前
《事件关系阴阳博弈动力学:识势应势之道》第四篇:降U动力学——认知确定度的自驱演化
人工智能·科技·神经网络·算法·架构
AI科技星3 小时前
全域数学·72分册:场计算机卷【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
科研前沿4 小时前
镜像孪生VS视频孪生核心技术产品核心优势
大数据·人工智能·算法·重构·空间计算
水蓝烟雨4 小时前
1931. 用三种不同颜色为网格涂色
算法·leetcode
晨曦夜月5 小时前
map与unordered_map区别
算法·哈希算法
图码5 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻
handler015 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
minglie15 小时前
实数列的常用递推模式
算法
代码小书生6 小时前
math,一个基础的 Python 库!
人工智能·python·算法
AI科技星6 小时前
全域数学·数术本源·高维代数卷(72分册)【乖乖数学】
人工智能·算法·数学建模·数据挖掘·量子计算