差分数组 前缀和(学习笔记)

前缀和

一维前缀和:

\(sum[i]=sum[i-1]+a[i]\)

二维前缀和:

\(S[i,j]=S[i-1,j]+S[i,j-1]-S[i-1,j-1]+A[i,j]\)

前缀和也没什么好讲的理论知识,做题会用就行了!

P2280 [HNOI2003]激光炸弹

https://www.luogu.org/problemnew/show/P2280

蒟蒻太懒了,不想手打题面,就直接分析吧

很显然,这道题是二维前缀和的运用;

我们可以先递推求出二维前缀和S,然后枚举边长为R的正方形的下标(右下角的下标),然后就可以直接得到正方形内所有目标的价值之和,并更新最大值

#include<bits/stdc++.h>
using namespace std;
int n,r,s[5021][5021],ans,x,y,val;
int main(){
    scanf("%d%d",&n,&r);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&x,&y,&val);
        s[x+1][y+1]=val;
    }
    for(int i=1;i<=5001;i++)
    for(int j=1;j<=5001;j++)
           s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];//递推求出二维前缀和
    for(int i=0;i<=5000-r;i++)
    for(int j=0;j<=5000-r;j++)
          ans=max(ans,s[i+r][j+r]-s[i+r][j]-s[i][j+r]+s[i][j]);
    //枚举每个正方形,直接计算得到该正方形的价值
    printf("%d\n",ans);
    return 0;
}


P2879 [USACO07JAN]区间统计Tallest Cow

https://www.luogu.org/problemnew/show/P2879

这个题面我没找到中文的,我还是太蒻(lan)了

我们可以建立一个数组c,起初数组中全为零(即看做每头牛都一样高),当有一条关系表示a和b可以互相看见时,则把数组c中下标区间为[a+1,b-1]的数都减去1(注意到这里就可以用我们上述讲到的差分数组的优化方法),因为第P头牛是最高的,则c[P]最后一定为零,第i头牛的身高就等于c[i]+H;

#include<bits/stdc++.h>
#define ll long long
#define gt getchar()
using namespace std;
inline int read(){
    int k=0;char ch=gt;
    while(ch<'-')ch=gt;
    while(ch>'-')k=k*10+ch-'0',ch=gt;
    return k;
}
int n,p,h,m;
map<pair<int,int>,bool> existed;
int c[10005],d[10005];
int main(){
    n=read();p=read();h=read();m=read();
    for(int i=1;i<=m;i++){
    	int a,b;
    	a=read();b=read();
    	if(a>b) swap(a,b);
        //始终使第a头牛的身高比第b头牛高
    	if(existed[make_pair(a,b)]) continue;
        //检查关系是否重复出现,判重
    	d[a+1]--;d[b]++;
        //差分数组优化
    	existed[make_pair(a,b)]=true;
        //标记
    }
    for(int i=1;i<=n;i++){
    	c[i]=c[i-1]+d[i];
        //d数组是c数组的差分数组
    	printf("%d\n",h+c[i]);
    }
    return 0;
}

差分数组

差分数组:记录当前位置的数与上一位置的数的差值.

如果我们在差分数组的\(b_x\)加上\(val\),在\(b_{y+1}\)位置处减去\(val\),就能达到整个区间修改的操作.

这里可能不太好理解,但其实这就是一个结论,我们来举个例子:

现在有一个序列A:1 4 2 3 5

则其差分数组B:(1) 3 -2 1 2

现在我们要使序列A区间\([2,4]\)上的每个值都加2(我习惯数组从下标1开始记录)

对于序列A我们有常规操作得到 1 6 4 5 5

对于修改后的序列A,其新的差分数组为 (1) 5 -2 1 0

如果我们直接按照上面的结论对差分数组进行更改,则修改后的差分数组为(1) 5 -2 1 0;

于是我们发现这两种方法得到的差分数组是一样的!!!

根据时间复杂度和修改的难易程度,我们当然会选择后者进行更改,因为它只需要修改差分数组的两个位置的值;

代码实现:

#include<bits/stdc++.h>
using namespace std;
int n,m,q,last,sum[10086],b[10086],s[10086];
//b[]数组是对于原序列的差分数组
//s[]数组是通过差分数组可以得到原序列
//sum[]数组是前缀和数组
int main(){
    cin>>n;//n个数 
    for(int i=1,x;i<=n;i++){
       	cin>>x;//这里实际上不需要a数组,视题而异 
        b[i]=x-last;//得到差分数组 
        last=x;//别忘了变化last变量 
    }
    cin>>m;//m次操作
    for(int i=1,l,r,val;i<=m;i++){
         cin>>l>>r>>val;//在[l,r] 加上val
         b[l]+=val,b[r+1]-=val; 
    }
    for(int i=1;i<=n;i++){
        s[i]=s[i-1]+b[i];//这里是处理s数组 
        sum[i]=sum[i-1]+s[i];//处理sum数组. 
    }
    cin>>q;//q个询问 
    for(int i=1,l,r;i<=q;i++){
        cin>>l>>r; //询问[l,r]的区间和.
        cout<<sum[r]-sum[l-1]<<endl; //输出
    }
}

模板题: AT2442 フェーン現象 (Foehn Phenomena)

你知道N+1个地点的海拔Ai,编号为0…N,有风从0吹向N,想让你求出地点N的风的温度.

第一行输入包括四个被空格隔开的整数N,Q,S,T.这表示JOI先生在地点N有一所房子,有Q次地壳运动,海拔每上升1米的话,风的温度会降低S度,海拔每下降一米的话,风的温度会上升T度.

接下来的N+1行中第i行(1≤i≤N+1)包含一个整数Ai−1,表示地壳运动前地点i−1的海拔高度.接下来的Q行中第j行((1≤j≤Q)包括三个被空格隔开的整数Lj,Rj,Xj.这表示第j天地壳运动使地点Lj到地点Rj中这些地点的海拔变化了Xj;

#include<bits/stdc++.h>
#define N 200005
#define R register
using namespace std;
long long n,q,s,t;
long long A[N],last,ans;
inline void read(long long &x){
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}//快读优化
inline long long get(long long x){
    return  x > 0 ? -(x*s) : -(x*t) ; 
}//定义函数get计算风的温度的变化
int main(){
    read(n),read(q),read(s),read(t);
    read(last);
    for(R int i=1;i<=n;i++){
        R long long x;
        read(x);
        A[i]=x-last;//差分数组
        last=x;//不断更新last
        ans+=get(A[i]);//ans初始化N点的温度
    }
    for(R long long x,y,z;q;q--){
        read(x),read(y),read(z);
        ans-=get(A[x]);
        A[x]+=z;
        ans+=get(A[x]);
        if(y!=n){
        //差分数组改变y+1的值,所以判断一下
        	ans-=get(A[y+1]);
            A[y+1]-=z;
        	ans+=get(A[y+1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

posted on 2018-10-27 15:28  PPXppx  阅读(363)  评论(0编辑  收藏  举报