算法竞赛进阶指南0.3 前缀和与差分

注:此博客为学习acwing视频的随手的笔记

目录

1.激光炸弹 (二维前缀和) 

2. IncDec序列 (差分

3. Tallest Cow (差分

 

 

前缀和与差分

前缀和与差分是一个互逆的运算。

 

二维前缀和:

1)

for(int i = 1 ; i <= n ; i ++)     //二维前缀和

        for(int j = 1 ; j <= m ; j ++)

            sum[i][j] += sum[i -  1][j] + sum[i][j - 1] - sum[i - 1][j - 1];

 

2)

for(int i=1;i<=n;i++)

        for(int j=1;j<=m;j++)

            sum[i][j] +=sum[i][j-1];

    for(int i=1;i<=n;i++)

        for(int j=1;j<=m;j++)

            sum[i][j] += sum[i-1][j];

 

1.激光炸弹 (二维前缀和)

 

#include <iostream>

#include <cstdio>

 

using namespace std;

const int maxn = 5005;

typedef long long ll;

int N , R ;

int sum[maxn][maxn];

int main(){

    cin.sync_with_stdio(false);

    cin.tie(0);

    cin >> N >> R;

    int n = R , m = R;

    for(int i = 0 ; i < N ; i ++){

        int x, y, z;

        cin >> x >> y >> z;

        sum[++x][++y] =  z;  //从 1 开始 不需要再处理边界问题

        n = max(n , x) , m = max(m , y);  //这道题的长宽需要自己判

    }

    for(int i = 1 ; i <= n ; i ++)     //二维前缀和

        for(int j = 1 ; j <= m ; j ++)

            sum[i][j] += sum[i -  1][j] + sum[i][j - 1] - sum[i - 1][j - 1];

 

    int ans = 0;

    for(int i = R ; i <= n ; i ++)

        for(int j = R ; j <= m ; j++)

            ans = max(ans , sum[i][j] - sum[i - R][j] - sum[i][j - R] + sum[i - R][j - R]);

    cout << ans << endl;

    return 0;

}

 

2. IncDec序列 (差分)

差分: 前缀和的逆运算 

a[1] , a[2] , a[3] , a[4] , ... , a[n]

b[i] = a[i] - a[i - 1]  b[1] = a[1]

ba的差分序列 ab的前缀和序列

 

O(1) 给区间[l , r]加上一个常数C :

b[l] += C , b[r + 1] -= C

它的效果就是前缀和序列al ~rC 其它不影响

 

 

 

  1. 先求出序列的差分序列b
  2. 做以下操作 使得 b[2] ~b[n]变为0

1) 选择两个数 2 <= i j <= n   b[i] += 1  b[j] -= 1 b[i] -= 1  b[j] += 1

2) i = 1 , 2 <= j <= n    

3) 2 <= i <= n , j = n + 1

4) i = 1 , j = n + 1 (无意义)

贪心1) 操作 前面加1 后面-1这样的    (可以保证最小次数)

 

操作1最多能够选择 min(差分序列正数和 , 差分序列负数绝对值和)

若正数多了 则进行操作二  操作三 均可

例如:操作1结束后 , 正数还剩5 ,那么可以选择

0次操作二 , 5次操作三 1次操作二 , 4次操作三

2次操作二 , 3次操作三 3次操作二 , 2次操作三

4次操作二 , 1次操作三 5次操作二 , 0次操作三

六种方式 六种结果

 

综上 : 最小操作次数 = 操作1 + 操作2,3

= min(正数的和 , 负数的和的绝对值) + abs(正数的和-负数的和的绝对值)

方案数 =   abs(正数的和-负数的和的绝对值) + 1

 

代码

#include <iostream>

#include <algorithm>

 

using namespace std;

const int maxn = 100050;

int a[maxn];

 

typedef long long ll;

 

int main(){

    cin.sync_with_stdio(false);

    cin.tie(0);

    int n;

    cin >> n;

    for(int i = 1 ; i <= n ; i ++) cin  >> a[i];

    for(int i = n ; i > 1 ; i --) a[i] -=  a[i - 1];   // 求差分序列

    ll pos = 0 , neg = 0; //pos 正数和  neg 负数和

    for(int i = 2 ; i <= n ; i++){

        if(a[i] > 0 ) pos += a[i];

        else neg -= a[i];

    }

    cout << min(pos , neg) + abs(pos - neg) <<endl;

    cout << abs(pos - neg) + 1 <<endl;

    return 0;

}

 

3. Tallest Cow (差分)

分析 :任意一对Ai , Bi不可能发生交叉

 

只可能:

 

 

问题转换成 一个序列所有元素都是 H 的序列 , 给的每一个关系A B(A, B)之间的元素都 -1 ,问每个数最终是多少

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <set>

#include <cstring>

 

using namespace std;

 

const int maxn = 100005;

int n , i , h , r , sum[maxn];

set<pair<int,int> > s;

int main(){

    memset(sum , 0 , sizeof sum);

    cin >> n >> i >> h >> r;

    sum[1] = h;

    for(int i = 0 ; i < r ; i ++){

        int x , y;

        cin >> x >> y;

        if(x == y) continue;

        if(x > y) swap(x , y);

        if(s.find(make_pair(x,y))!=s.end()) continue;

        s.insert(make_pair(x,y));

        sum[x + 1] -= 1;

        sum[y] += 1;

    }

    for(int i = 1 ; i <= n ; i ++) sum[i] += sum[i - 1];

    for(int i = 1 ; i <= n ; i ++) cout << sum[i] <<endl;

}

 

posted @ 2019-07-24 22:12  zangzang  阅读(181)  评论(0编辑  收藏  举报