前缀和差分学习

1.前缀和、差分定义

1.1一维

1)前缀和:某项为 该项及该项前 的数据和

//数组a (下标为0开始)变前缀和数组
for(int i=1;i<n;i++)a[i]+=a[i-1];

2)差分:某项变为 该项与前一项的差
2).1用途:维护一个区间的快速修改
2).2性质:①差分序列求前缀和可得原序列
②将原序列区间【L,R】中的元素加q可以转化为L处+q,R+1处-q;

1.2二维

发现了一篇很好的东西(但是我感觉在二位差分那差一点点意思)
1)前缀和:
1).1定义:求一个矩阵内的从(0,0)到(i,j)的子矩阵的数的和
1).2用途:一般运用在动态规划

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

1).3推广:从(x1,y1)到(x2,y2)的矩阵和

sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][x1-1]

2)差分:
从(x1,y1)到(y1,y2)的子矩阵中每个值都 加a
eg:黄色区域中的值均加a,那么就是 红+绿+蓝+黄 中的值均加a,再 绿+红 中的值均减a,蓝+红 中的值均减a,最后 红中的值加一个a
在这里插入图片描述

df[x2+1][y2+1]+=a;df[x1][y1]+=a;
df[x1][y2+1]-=a;d[x2+1][y1]-=a;

1.3小小的总结

前缀和 主要用于对数据区间的查询
差分 主要用于对数据的区间维护(仅限加减)
(以后补一下 树上前缀和 与 树上差分)

2.习题

2.1一维

HJ浇花-前缀和+差分

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int M=1e6+10;
int n,l,r,m_r,df[M]cnt[200010];

int main(){
	ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
	cin>>n;
	m_r=0;
	for(int i=1;i<=n;i++){
		cin>>l>>r;
		df[l]++;df[r+1]--;//差分数组
		m_r=max(m_r,r+1);
	}
	for(int i=1;i<=m_r;i++)df[i]+=df[i-1];//前缀和变原数组
	for(int i=0;i<=m_r;i++)cnt[df[i]]++;
	cout<<cnt[1];
	for(int i=2;i<=n;i++)cout<<" "<<cnt[i];cout<<endl;
	return 0;
}

2.2二维

洛谷 P1387 最大正方形-二维前缀和的应用
此题数据小所以可用此法,一般用dp(待补)

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int M=1e6+10;
int n,m,prs[110][110],i,ans;

int main(){
	ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>prs[i][j];
			prs[i][j]+=prs[i-1][j]+prs[i][j-1]-prs[i-1][j-1];//前缀和
		}
	}
	//
	for(i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int l=1;l<=min(i,j);l++){//从当前格子开始正方形的判断及记录最大边长
				if(l*l== prs[i][j]-prs[i-l][j]-prs[i][j-l]+prs[i-l][j-l] )//二维前缀和的推广
					ans=max(l,ans);
				else break;
			}
		}
	}
	cout<<ans;
	return 0;
}

洛谷 P3397 地毯-二维差分

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int M=1e6+10;
int n,m,df[1010][1010];

int main(){
	ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		df[x1][y1]++;   df[x2+1][y2+1]++;
		df[x1][y2+1]--; df[x2+1][y1]--;
	}//区间维护-二维差分
	//取前缀和 变成原序列
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			df[i][j]+=df[i-1][j]+df[i][j-1]-df[i-1][j-1];
			if(j!=1)cout<<" "<<df[i][j];
			else cout<<df[i][j];
		}
		cout<<endl;
	}
	return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页