一维前缀和
从数组第一个开始累加
s[i]=s[i-1]+a[i]
求区间[l,r]
的和,O(1)
复杂度 sum=s[r]-s[l-1]
二维前缀和
递推
s[i][j]=s[i][j]-s[i-1][j]-s[i][j-1]+s[i-1][j-1]
例题:激光炸弹
一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。现在地图上有n(n≤10000)个目标,用整数xi,yi(0≤xi,yi≤5000)表示目标在地图上的位置,每个目标都有一个价值0<vi<100。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁。
将点向右下移动一次就可解决边界不包括的点
给出一个5000×5000的矩阵,每个格子上都有权值,求用一个n×n的框最多能框住多少?
用前缀和来求n*n
的框的价值
value=s[i][j]+s[i-r][j]+s[i][j-r]-s[i-r][j-r]);
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=5005;
int s[maxn][maxn];
int main(){
int n,r;
// memset(s,0,sizeof s);
freopen("a.txt","r",stdin);
cin>>n>>r;//n个数,正方形半径为r
// 没有注意到相同点会出现多次
// for(int i=0;i<n;i++){
// int a,b;
// cin>>a>>b;
// cin>>s[a+1][b+1];//向右下方移动1位
// }
for(int i=1;i<=n;i++){
int a,b,v;
cin>>a>>b>>v;
sum[a+1][b+1]+=v;
}
for(int i=1;i<=5001;i++){//第0默认为0
for(int j=1;j<=5001;j++){
s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];//二维前缀和
}
}
int ans=0;
for(int i=r;i<=5001;i++){
for(int j=r;j<=5001;j++){
ans=max(ans,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);//遍历所有存在的子区域
}
}
cout<<ans;
return 0;
}
差分
差分与前缀和是一种互逆的运算
数组 | 2 | 1 | 4 | 6 | 5 |
---|---|---|---|---|---|
前缀和 | 2 | 3 | 7 | 13 | 18 |
差分 | 2 | -1 | 3 | 2 | -1 |
- 差分前缀和是原数组
- 前缀和差分也是原数组
结论题
IncDec sequence(inc序列)
给定一个长度为n(n<=
1
0
5
10^5
105)的数列,每次选择区间 [l,r],使区间内的数加1或减1.
求最少需要多少次操作使的数列中所有的数都一样,并且数列可能有几种?
区间操作放在差分数组中就是改变2个数组的值,目标是,差分数组 b 2 b_2 b2~ b n b_n bn都为0,且数列最终由n个 b 1 b_1 b1组成的,那么不同的 b 1 b_1 b1出现的次数代表了不同的数列
四种操作
- 从 b 2 b_2 b2~ b n b_n bn中选两个分别+1,-1,或者-1,+1,减少正负数,使差分数组向0移动, (更快接近结果)
- 选 b 1 b_1 b1和 b j b_j bj (2<=j<=n),改变差分数组中某一个数( b 1 b_1 b1改变影响最终不同数列的次数,不影响操作的次数),
- 同理,选 b j b_j bj和 b n + 1 b_{n+1} bn+1, b n + 1 b_{n+1} bn+1代表一直改变到原数列的最后一个,所以还是改变了差分数组的一个值
- 改变 b 1 b_1 b1和 b n + 1 b_{n+1} bn+1,原数组全部+,- 1,没有实际意义
- 设 b 2 b_2 b2~ b n b_n bn正数总和p,负数绝对值总和q,(原则是走最少),第一步走min(q,p),剩下|q-p|个正数或者负数,
- 剩下的|p-q|执行2,3操作,那么恰好为|p-q|次
操作次数
ans=min(q,p)+|q-p|
而
min(p,q)+|q-p|=max(q,p)
证明
p>q
则min(p,q)=q
|q-p|=p-q
得到ans=q+p-q=p
而p=max(p,q)
- 同理
p<q
数列个数
b
1
b_1
b1一开始为1种,若2,3操作都为2操作,最多可能有|p-q|+1
种
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=pow(10,5)+1;
int a[maxn];
int b[maxn];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i];
b[i]-=b[i-1];
}
// for(int i=1;i<=n;i++)
// cout<<b[i]<< ' ';
int sum_1=0,sum_2=0;
for(int i=2;i<=n;i++){//b2开始统计正负
if(b[i]>0) sum_1+=b[i];
else sum_2+=b[i];//0无所谓
}
cout<<"操作次数"<<max(sum_1,sum_2);
cout<<"\n可能的不同数列个数"<<1+abs(sum_1-sum_2);
return 0;
}
Tallest Cow 最高奶牛 POJ3263
大意:N头牛站成一行,当且仅当2头牛之间所有牛都比它们矮,2头牛能相互看见,一直第p头牛最高,高为H,剩余N-1头牛不知道,给出m对关系(2头牛能够相互看见),求每头牛的最大可能身高? (1<=n,m<= 1 0 4 10^4 104,1<=h<= 1 0 6 10^6 106)
思路:因为i,j,两头牛能够相互看见,那么(i-1)~(j-1)
之间的所有牛的身高比他们小,假设所有牛的身高为0,那么(i-1)~(j-1)
之间牛的身高都-1,即b[i+1]-=1 b[j]+=1
最后通过前缀和求出原数组,每头牛可能的最高身高=H+a[i]
#include<iostream>
#include<map>
using namespace std;
const int maxn=10005;
int a[maxn];
int b[maxn];
map<pair<int,int>,bool> e;
int main(){
int n,p,h,m;
cin>>n>>p>>h>>m;
for(int i=1;i<=m;i++){
int k1,k2;
cin>>k1>>k2;
if(k1>k2) swap(k1,k2);
if( e[make_pair(k1,k2)] == true ) continue;
else{
e[make_pair(k1,k2)]=true;
b[k1+1]--;
b[k2]++;
}
}
for(int i=1;i<=n;i++){
a[i]=a[i-1]+b[i];
cout<<a[i]+h<<endl;
}
return 0;
}