前缀和
对于一个给定数列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=i−R+1i ∑ y = j − R + 1 j \sum_{y=j-R+1}^{j} ∑y=j−R+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;
}
}