• 前缀和


    前缀和

    前缀和是一种很重要的思想 其实这个思想很简单

    就是一次预处理 把原来O(N*M)的复杂度降到O(N+M)

    a[i]+...+a[j]=sum[j]-sum[i-1];

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline ll read() {
        ll x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)) (ch=='-')?(f=-1,ch=getchar()):ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        return x*f;
    }
    int n;
    const int N=1<<20;
    int a[N],sum[N];
    signed main(){
        memset(sum,0,sizeof(sum));//先清空数组
        n=read();
        for(register int i=1;i<=n;i++) a[i]=read();//读入
        for(register int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];//前缀和
    }

    那么区间的数值查询该怎么做呢

    假设有m次查询

    for(register int i=1;i<=m;i++) {
            int x=read(),y=read();
            ans=sum[y]-sum[x-1];
        }

    ans=∑a[x]+...a[y];

    如上所述

    a[x]+...a[y]=sum[y]-sum[x-1];

    想想看为什么要 -1 而不是 sum[y]-sum[x]

    举个例子:

    n=5

    i 1 2 3 4 5
    a[i] 5 3 4 2 1
    sum[i] 5 8 12 14 15

    看例子 自己体会吧...(我太菜了讲不明白

    接下来来几道例题

    例1

    Problem

    给出一个含有n个整数的数列a,并且有m次询问,每次询问数列在区间[l,r][l,r]内的和,即求a[l]+a[l+1]++a[r]的值。

    Input Data

    第一行为一个整数 T (1T50),表示共有T组输入数据;
    对于每组数据,第一行是两个正整数 n,m (1n100000 ,1m1000)分别代表数列长度和询问次数;
    第二行有 n 个正整数,第 i 个数表示数列元素 a[i]1a[i]109​​)的值;
    接下来 m 行,每行有两个正整数 lr (lrn),代表询问内容。

    Output Data

    每组数据输出 m 行,每行一个数为该次询问的区间和。
    保证数据都在64位正整数范围内。

    裸题

    #include<bits/stdc++.h>
    #define ULL unsigned long long
    #define MAXN 100000+5
    #define f(i,j,n) for(register int i=j;i<=n;i++)
    using namespace std;
    ULL T,a[MAXN],n,m;
    void solve(int T) {
        while(T--) {
            memset(a,0,sizeof(a));
            cin>>n>>m;
            f(i,1,n) {
                int x;
                cin>>x;
                a[i]=a[i-1]+x;
            }
            f(i,1,m) {
                int l,r;
                cin>>l>>r;
                cout<<a[r]-a[l-1]<<endl;
            }
        }
    }
    int main() {
    
        cin>>T;
        solve(T);
        return 0;
    }
    例1

    例2

    Problem

    给定n个整数构成的序列,所有相邻的m个数有nm+1个,求其中的最小值。

    Input Data

    第一行,2个整数n和m,范围在[3100000],n>m;
    第二行,有n个正整数,范围在[31000]。

    Output Data

    一个整数,表示最小值。

    这题是通过预处理之后再进行枚举(也是前缀和的通常做法)

    #include<bits/stdc++.h>
    #define ULL unsigned long long
    #define MAXN 100000+5
    #define f(i,j,n) for(register int i=j;i<=n;i++)
    #define INF 2147483647
    using namespace std;
    ULL a[MAXN],n,m,MIN=INF;
    void solve(int T) {
        while(T--) {
            memset(a,0,sizeof(a));
            cin>>n>>m;
            f(i,1,n) {
                int x;
                cin>>x;
                a[i]=a[i-1]+x;
            }
            f(i,m,n) MIN=min(MIN,a[i]-a[i-m]);
            cout<<MIN<<endl;
        }
    }
    int main() {
        solve(1);
        return 0;
    }
    例2

    例3

    在一个nm的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。

    Input Data

    第一行为两个整数n,m1n,m100),接下来n行,每行m个数字,用空格隔开,0或1。

    Output Data

    一个整数,最大正方形的边长

    #include<bits/stdc++.h>
    #define ULL unsigned long long
    #define MAXN 100+5
    #define f(i,j,n) for(register int i=j;i<=n;i++)
    using namespace std;
    ULL T,a[MAXN][MAXN],n,m;
    int q(int i,int j,int k,int l) {
        f(p,i,j)
        f(q,k,l)
        if(a[p][q]==0) return 0;
        return min(j-i+1,l-k+1);
    }
    void solve(int T) {
        int ans=0;
        while(T--) {
            memset(a,0,sizeof(a));
            cin>>n>>m;
            f(i,1,n)
            f(j,1,m) {
                int x;
                cin>>x;
                a[i][j]=x;
            }
            f(i,1,n)
            f(j,i,n)
            f(k,1,m)
            f(l,k,m)
            ans=max(ans,q(i,j,k,l));
            cout<<ans<<endl;
        }
    }
    int main() {
        solve(1);
        return 0;
    }
    例3

    例4

    拓展(二维前缀和)

    #include<bits/stdc++.h>
    #define f(i,j,n) for(register int i=j;i<=n;i++)
    using namespace std;
    const int N=100;
    int n,m;
    int a[N][N];
    int main() {
        scanf("%d",&n,&m);
        f(i,1,n)
        f(j,1,m) {
            int x;
            scanf("%d", &x);
            a[i][j]=x+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
        }
        int x1, y1, x2, y2;
        while(~scanf("%d%d%d%d",&x1,&y1,&x2,&y2)) printf("%d
    ",a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1]);
        return 0;
    }
    二维前缀和

    BZOJ1218
    HNOI2003

    Problem

    一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。
    现在地图上有n(n10000)个目标,用整数Xi​​,Yi​​(其值在[0,5000][0,5000])表示目标在地图上的位置,每个目标都有一个价值。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和xy轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁。

    Input Data

    输入文件的第一行为正整数n和正整数R
    接下来的n行每行有3个正整数,分别表示xi​​,yi​​,vi​​

    Output Data

    输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。

    #include <bits/stdc++.h>
    #define f(i,j,n) for(register int i=j;i<=n;i++)
    #define M 5010
    using namespace std;
    int n,r,ans,sum[M][M];
    int main() {
        int x,y,z;
        cin>>n>>r;
        f(i,1,n) scanf("%d%d%d",&x,&y,&z),sum[x+1][y+1]+=z;
        f(i,1,5000) f(j,1,5000) sum[i][j]+=sum[i-1][j];
        f(i,1,5000) f(j,1,5000) sum[i][j]+=sum[i][j-1];
        f(i,r,5000) f(j,r,5000) ans=max(ans,sum[i][j]+sum[i-r][j-r]-sum[i][j-r]-sum[i-r][j]);
        cout<<ans<<endl;
        return 0;
    }
    例4

    (代码都是好久以前写的了 码风可能不太好 见谅)

    不存在十全十美的文章 如同不存在彻头彻尾的绝望
  • 相关阅读:
    css 中关于border-color 继承了color的说明
    关于飞书的相关开发案例
    好看的点赞设计
    较好的设计网站参考
    较好的B端设计体验 说明
    基于vue的流程图插件
    较好看的中后台模板(ui)
    前端笔记-201807
    前端笔记(201905-201906)
    前端笔记(201902-201904)
  • 原文地址:https://www.cnblogs.com/qf-breeze/p/10340331.html
Copyright © 2020-2023  润新知