CCF NOI1101(子矩阵求和)

题目描述

给出一个n行m列的矩阵,矩阵的每个位置有一个非负整数a[i][j],有q次询问,每次询问求一个左上角为(a,b),右下角为(c,d)的子矩阵的所有数之和。

输入

第一行两个整数n,m,表示矩阵的行和列的大小
接下来n行每行m个整数,为矩阵内容
接下来一行为一个整数q,表示询问次数
接下来q行每行4个整数a,b,c,d,含义见题面。

输出

共q行,第i行为第i个询问的答案。

样例输入

3 5
1 2 3 4 5
3 2 1 4 7
2 4 2 1 2
3
1 1 3 5
2 2 3 3
1 1 3 3

样例输出

43
9
20

数据范围限制

n*m<=100,000,a[i][j]<=1000,q<=100,000,1<=a<=c<=n,1<=b<=d<=m

分析:map[i][j]表示左上角为(1,1),右下角为(i,j)的子矩阵的所有数之和,

map[i][j]=map[i][j-1]+map[i-1][j]-map[i-1][j-1]+x,x是矩阵中点(i,j)上的数。

#include<cstdio>
#include<vector>
using namespace std;
vector<int> map[100100];
int main()
{
    int N,M,Q,x;
    scanf("%d%d",&N,&M);
    for(int i=0;i<=M;i++) map[0].push_back(0);//初始化
    for(int i=1;i<=N;i++)
    {
        map[i].push_back(0);
        for(int j=1;j<=M;j++)
        {
            scanf("%d",&x);
            map[i].push_back(map[i][j-1]+map[i-1][j]+x-map[i-1][j-1]);
        }
    }
    int x1,y1,x2,y2;
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int ans=map[x2][y2]-map[x2][y1-1]-map[x1-1][y2]+map[x1-1][y1-1];
        printf("%d
",ans);
    }
    return 0;
}
View Code

还有一种更快的方法,把以上map数组从0到N*M编号,

把二维数组变为一维(n*m<=100,000)。

#include<cstdio>
int sum[200000];
int main()
{
    int N,M,x,Q;
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=M;j++)
        {
            scanf("%d",&x);
            sum[i*M+j]=sum[(i-1)*M+j]+sum[i*M+j-1]-sum[(i-1)*M+j-1]+x;
        }
    }
    int x1,y1,x2,y2;
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d
",sum[x2*M+y2]-sum[x2*M+y1-1]-sum[M*(x1-1)+y2]+sum[M*(x1-1)+y1-1]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/ACRykl/p/8342537.html