前缀和 and 差分

前缀和

一维前缀和:
预处理: \(O(n)\)

\(S[i] = a[1] + a[2] + ... a[i]\)

求区间[L,R]的和:\(O(1)\)

\(a[ L ]+...+a[ R ] = S[r] - S[l - 1]\)

二维前缀和:.
预处理: \(O(nm)\)

S[i, j] = 第i行j列格子左上部分所有元素的和

求以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和: \(O(1)\)

\(S[x_2 , y_2 ]-S[x_1 -1 , y_2 ]-S[x_2 , y_1 -1]+S[x_1 -1 , y_1 -1]\)

image

模板题

P2280 [HNOI2003]激光炸弹
洛谷
acwing

题意:
地图上有 N 个目标,,每个目标都有一个价值 Wi。每个炸弹有个范围,求一颗炸弹最多能炸掉地图上总价值为多少的目标。
思路:
二分前缀和
代码:

const int N = 5010;
int s[N][N];
int n,r;
void solve()
{
    cin>>n>>r;
      r = min(r, 5001);
    for(int i=0;i<n;i++){
        int x,y,w;
        cin>>x>>y>>w;
        s[x+1][y+1]+=w;
    }
    //预处理前缀和
    for(int i=1;i<=5001;i++)
       for(int j=1;j<=5001;j++)
         s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];

    int ans=0;
    for(int i=r;i<=5001;i++)
         for(int j=r;j<=5001;j++)
             ans=max(ans,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);
    cout<<ans<<endl;
}

差分

思路:

  • 想要让\([l, r]\)上面整体加上\(c\),在一个长的数组上是无法实现单方面让\([l, r]\)上加
  • 只有先让 \(l\) 右边的所有数先加上\(c\), 在让 \(r + 1\)右边的减去 \(c\), 这样就可以让\([l, r]\)上加上\(c\)

代码:

int insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
}

应用

增减序列
洛谷
acwing
原文
题意:
给定一个长度为 n 的数列 \(a1,a2,…,an\),每次可以选择一个区间 \([l,r]\),使下标在这个区间内的数都加一或者都减一。

  1. 至少需要多少次操作才能使数列中的所有数都一样
  2. 并求出在保证最少次数的前提下,最终得到的数列可能有多少种

分析:
先求出差分数组
\(B[1]=A[1]\)
\(B[i]=A[i]−A[i−1]\) \((2<=i<=n)\)

很明显题意1在差分数组的体现就是:\(B_2 ... B_n\) 都为零,B1取值无所谓。

贪心:

  1. 我们可以,每一次选取\(B_i 和B_j\)\(2<=i,j<=n\),而且这两个数,一个为正数,一个为负数

为什么要是正负配对?因为我们是要这个B序列2~n都要为0,所以这样负数增加,正数减少,就可以最快地到达目标为0的状态。

  1. 至于那些无法配对的数\(B_k\)可以选\(B_1\)或者\(B_{n+1}\),这两个不影响的数,进行修改。

所以说最少操作数就是\(min(p,q)+abs(p−q)=max(p,q)\)
p为b序列中正数之和,而q为b序列中负数之和

我们知道经过操作后,数组的值就是b[1]的值。因此数组的情况就是b[1]可能的取值。然后我们贪心过程中,就只有第二步会改变b[1]的值,所以
最终序列a可能会有\(abs(p−q)+1\)种情况。

代码:

const int N = 100010;

typedef long long LL;

int n;
int a[N], b[N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ ) b[i] = a[i] - a[i - 1];

    LL p = 0, q = 0;
    for (int i = 2; i <= n; i ++ )
        if (b[i] > 0) p += b[i];
        else q -= b[i];

    cout << max(p, q) << endl;
    cout << abs(p - q) + 1 << endl;

    return 0;
}

posted @ 2022-08-08 09:10  kingwzun  阅读(191)  评论(0编辑  收藏  举报