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


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

目录

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

2. IncDec序列 (差分)

3. Tallest Cow (差分)

前缀和与差分

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

二维前缀和:

1)

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

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

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

2)

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

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

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

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

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

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

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

#include <iostream>

#include <cstdio>

usingnamespacestd;

constintmaxn = 5005;

typedeflonglongll;

intN , R ;

intsum[maxn][maxn];

intmain(){

cin.sync_with_stdio(false);

cin.tie(0);

cin >> N >> R;

intn = R , m = R;

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

intx, y, z;

cin >> x >> y >> z;

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

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

}

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

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

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

intans = 0;

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

for(intj = 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;

return0;

}

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>

usingnamespacestd;

constintmaxn = 100050;

inta[maxn];

typedeflonglongll;

intmain(){

cin.sync_with_stdio(false);

cin.tie(0);

intn;

cin >> n;

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

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

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

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

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

elseneg -= a[i];

}

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

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

return0;

}

3.Tallest Cow (差分)

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

只可能:

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

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <set>

#include <cstring>

usingnamespacestd;

constintmaxn = 100005;

intn , i , h , r , sum[maxn];

set<pair<int,int> > s;

intmain(){

memset(sum , 0 , sizeofsum);

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

sum[1] = h;

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

intx , 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(inti = 1 ; i <= n ; i ++) sum[i] += sum[i - 1];

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

}

优质内容筛选与推荐>>
1、POJ-2406 Power Strings (KMP)
2、[分享]VS2003插件破解版..仿VS2005功能
3、【原创】大数据基础之Zookeeper(4)应用场景
4、Nginx的负载均衡和项目部署
5、redis教程


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号