zoukankan      html  css  js  c++  java
  • 差分数组 前缀和(学习笔记)

    前缀和

    一维前缀和:

    (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
    ",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
    ",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
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    库引入问题
  • 原文地址:https://www.cnblogs.com/PPXppx/p/9861383.html
Copyright © 2011-2022 走看看