0x03 前缀和与差分

前缀和

对于一个给定数列A

  • 前缀和数列 S[i]= ∑ j = 1 i \sum_{j=1}^{i} j=1iA[j]
  • 部分和即某区间内数的和可表示为前缀和相减的形式
    sum(l,r)= ∑ i = l r \sum_{i=l}^{r} i=lrA[i]=S[r]-S[l-1]

二维数组:

  • 前缀和数列 S[i][j]= ∑ x = 1 i \sum_{x=1}^{i} x=1i ∑ y = 1 j \sum_{y=1}^{j} y=1jA[x][y]
    S[i][j]=S[i-1][j]+S[i][j-1]-S[i-1][j-1]+A[i][j]

  • 对于任意一个边长为R的正方形
    ∑ x = i − R + 1 i \sum_{x=i-R+1}^{i} x=iR+1i ∑ y = j − R + 1 j \sum_{y=j-R+1}^{j} y=jR+1jA[x][y]=S[ i ][ j ]-S[ i-R ][ j ]-S[ i ][ j-R ]+S[ i-R][ j-R]

例:神奇炸弹
在这里插入图片描述代码:

#include<bits/stdc++.h>
using namespace std;
int a[5005][5005];
int main()
{
	int N,r;cin>>N>>r;
	int n=r,m=r;//边长最小设置为R 
	for(int i=0;i<N;++i)
	{
		int x,y,w;
		cin>>x>>y>>w;
		x++;y++;//使坐标从1开始,避免边界问题的讨论 
		n=max(n,x),m=max(m,y);//找到目标范围的边长 
		a[x][y]+=w; 
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
			//为了节省空间将前缀和直接加到原数组中 
		}
	 } 
	 int res=0; 
	 for(int i=r;i<=n;++i)
	 {
	 	for(int j=r;j<=m;++j)
	 	{
	 		res=max(res,a[i][j]-a[i-r][j]-a[i][j-r]+a[i-r][j-r]);
		 }
	 }
	 cout<<res<<endl;
 } 

差分

给定数列A
其差分数列B:

  • B[1]=A[1]
  • B[ i ]=A[ i ]-A[ i-1 ] (2<=i<=n)

前缀和与差分是一对互逆运算:
差分的前缀和是原序列,前缀和的差分也是原序列

将区间操作转化为单点操作:
把序列A的区间[l,r]加d(即将Al,Al+1````Ar都加上d),其差分序列B的变化为Bl加d,Br+1减d,其他位置不变

例题:IncDec Sequence
在这里插入图片描述分析:
将区间操作转化为单点操作:利用差分性质
将对A序列的+1,−1让A序列相同的操作,改为将B2,…,Bn变成全0即可,也就是A序列全部相等。

贪心:

  • 为了尽快清0差分B数组,每一次选取Bi和Bj(2<=i,j<=n)使这两个数,一个为正数,一个为负数,每一次区间操作即是对Bi,Bj的一加一减操作:正负配对,使负数增加,正数减少,可以最快地到达目标为0的状态。
  • 无法配对的数Bk,可以选B1或者Bn+1,这两个不影响的数,进行修改。
  • 因此最少操作数=min(p,q)+abs(p−q)=max(p,q),最终序列a可能会有abs(p−q)+1种情况。(p为b序列中正数之和,而q为b序列中负数之和)

代码:

#include<bits/stdc++.h>
using namespace std;
long long  a[100005];
int main()
{
	int n;cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>a[i]; 
		
	}
	long long res=0,ans=0;
	for(int i=2;i<=n;++i)
	{
		long long c=a[i]-a[i-1];
		if(c>0) res+=c;
		else ans-=c;
	}
	cout<<min(ans,res)+abs(ans-res)<<endl;
	cout<<abs(res-ans)+1<<endl;
}

Tallest Cow
在这里插入图片描述
分析:
M对关系表明牛的相对身高关系,即对于给定m对a,b牛之间的牛身高必然至少比a,b低1,因此我们建立一个全为0的数组c,起始每牛身高为0,ab牛之间的牛都将其身高减一,以表明该关系,此操作可以利用差分数组实现,开始时全0数组c的差分数组d也应该全为0 ,若a,b牛存在关系,即a+1~b-1头牛身高减一,也即d[a+1]–;d[b]++
以此类推,最高的牛最后应仍保持为0,其身高为H,而其他牛相对其身高可以用H+c[i]表示

代码:

#include<bits/stdc++.h>
using namespace std;
long long a[10005];
map<pair<int,int> ,bool>vis;
int c[10010],d[10010];
int main()
{
	int n,p,h,m;cin>>n>>p>>h>>m;
	for(int i=0;i<m;++i)
	{
		int a,b;cin>>a>>b;
		if(a>b) swap(a,b);
		if(vis[make_pair(a,b)]) continue;
		//如果a,b关系已经输入过了,不必再操作 
		d[a+1]--;d[b]++;//a~b之间即a+1—b-1区间减一 
		vis[make_pair(a,b)]=true;//表明a,b关系已操作 
	}
	for(int i=1;i<=n;++i)
	{
		c[i]=c[i-1]+d[i];//差分的前缀和 
		cout<<h+c[i]<<endl;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值