前缀和与差分

一,前缀和

1,定义

前缀和可以简单理解为数列前n项和(多维一个道理)

以二维前缀和为例子:sum[i][j]=sum[i-1][j]+sum[i][j-1]+sum[i-1][j-1]+a[i][j](a[i][j]为这个格子的数值)(+sum[i-1][j-1]是因为减了这个重复的)

2,例题:P1387 最大正方形

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
const int INF = 0x3f3f3f3f;
const int N = 110;

//题目描述
//在一n×m 的只包含 0 和 1 的矩阵里找出一个不包含 0 的最大正方形,输出边长。
//二维前缀和,符合的正方形无疑问其所有格子和为l*l(因为有l*l个1),那么我们只需要遍历<=min(n,m)的边长,找到符合数字和为l*l的正方形就好
int a[N][N];
int sum[N][N];
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)for (int j = 1; j <= m; ++j)
			{
				cin >> a[i][j];
				sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
			}
	int len = min(n, m);
	int l = 2;
	int ans = 1;
	while (l <= len)
		{
			for (int i = l; i <= n; ++i)for (int j = l; j <= m; ++j)
					{
						if (sum[i][j] - sum[i - l][j] - sum[i][j - l] + sum[i - l][j - l] == l * l)ans = max(ans, l);
					}
			l++;
		}
	cout << ans << endl;
}

二,差分

1,定义,可以认为,差分是前缀和的逆运算

设数组为a[i],其差分数组为b[i],那么b[i]=a[i]-a[i-1]

例子:如果我们让a数组[l,r]上加1,那么其差分数组b会发生什么,观察到b[l]+1,b[l+1]~~b[r]不变,b[r+1]-1

它可以维护多次对序列的一个区间加上一个数,并在最后询问某一位的数或是多次询问某一位的数。

对数组a一段的操作,可以转化为只对差分数组b上俩个点的操作

如我让a[l,r]这段区间+1,那么等价于b[l]+1,b[r+1]-1

例题:P3128 [USACO15DEC]Max Flow P

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>
using namespace std;
#define ll     long long
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 100;
//题目描述
//给定一个长度为n的数列,每次可以选择一个区间[l,r],使这个区间内的数都加 1 或者都减 1。
//请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。
//思路
//对一个区间操作,其实等价与对差分b[l]与b[r+1]这俩个点操作,
//最终数列全部相等,其实就是除了b[1],剩下b数列为0,即我多次对不同区间操作,等价多次对差分数值b操作,使最终b[2]到b[n]的值为0
//容易想到,无论数列a加一还是减一,对差分数组b都是一个点+1,一个点-1,那么我们每次找俩个点(b[i]与b[j],让一个正一个负),让正数-1,负数+1,这样一步就等于走了俩步,直到只剩正数或者负数,这时候再一步步走(就是对这个点与b[1]一起操作,而b[1]走的情况决定了种类)
//存在几个数列,因为b[1]不用为0,且b[1]=a[1]=......=a[n],所以,存在几个,就是b[1]能有几种,因为前面正负倆步走不用对b[1]操作,只有一步一步才有可能,这一步可以与b[1]一起操作,也可以在自己的区间[i,i]里面操作,这样就决定b[1]变还是不变,所以种类就是abs(zhen-fu)+1(+1是因为自己算一种)
ll a[N];
int main()
{
	int n;
	cin >> n;
	ll zhen = 0, fu = 0;
	for (int i = 1; i <= n; ++i)
		{
			cin >> a[i];
			if (i >= 2)
				{
					ll c = a[i] - a[i - 1];
					if (c > 0)zhen += c;//记录大于0的差分,统计
					if (c < 0)fu -= c;//记录小于0的差分,统计
				}
		}
	ll ans1 = max(zhen, fu) ; //我们需要最小操作步数是min(zhen,fu)+abs(zhen-fu)等价于max(zhen,fu)
	ll ans2 = abs(zhen - fu) + 1;
	cout << ans1 << '\n' <<  ans2 << endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值