前缀和,二维前缀和!

前缀和

定义

用空间换取效率,做一个预处理,然后可以\(O(1)\)的查询某个区间的值的和。

实现

设\(s_i\)为第\(i\)个数\(a_i\)的前缀和,则\(s_i=s_i-1+a_i\)

当要查找区间的和时只要把对应的起点终点的元素相减即可。

例题

Educational Codeforces Round 30B Balanced Substring

翻译

给你一个长度至多为\(100000\)的\(01\)串,其中含有相同\(0\),\(1\)个数的子串被称为“平衡串”,问你最长的平衡串的长度。

思路

第一直觉是暴力,第二直觉看到数据范围不可行,第三直觉是绝望,第四直觉是前缀和。

一道前缀和的题目,遇到\(1\)就加\(1\),遇到\(0\)就减\(1\)。如果某个终点减去某个起点的值为\(0\),那么就记录并且选择最大的答案。不过有个问题就是起点和终点使用双层循环枚举会妥妥的\(TLE\),于是考虑使用\(map\)标记起点。

Code

#include<bits/stdc++.h>
using namespace std;
map<int,int> mp;
char s[100001];
int main()
{
    int n;
    cin>>n;
    cin>>s+1;
    int ans=0,sum=0;
    mp[0]=0;
    for (int i=1; i<=n;i++)
    {
        if (s[i]=='1')sum++;
        else sum--;
        if (mp.find(sum)==mp.end())
            mp[sum]=i;
        else
            ans=max(ans,i-mp[sum]);
    }
    cout<<ans<<endl;
    return 0;
}

二维前缀和

定义

基本和普通的前缀和差不多,不过二维前缀和是给你一个矩阵的一部分(右上角和左下角),让你查询这一部分的元素的和。

实现

如果直接暴力枚举,那么效率低低下。所以宝宝总结了一个公式:

\(x\)减,\(y\)减,\(xy\)都减。(记录)

不减,都减,减混合减。(查询)

正常描述就是:

首先预处理处以所有点为右下角,\((1,1)\)为左上角的矩阵中的元素和(记录)。

接着\((x1,y1)\)为右下角,\((x2,y2)\)为左上角的矩形中的元素和为:

\(f_{x_1y_1}+f_{x_2-1 y_2-1}-f_{x_1y_2-1}-f_{x_2-1y_1}\)

例题

前方高能!\(AGC\)的\(C\)题!

AGC 15 C Nuske vs Phantom Thnook

翻译

\(Nuske\) 现在有一个 \(N*M(N,M<=2000)\) 的矩阵 \(S\) , 若 \(S_i,j=1\) , 那么该处为蓝色, 否则为白色, 保证所有蓝色格子之间只有一条路。

给出 \(Q(Q<=200000)\) 次询问, 每次询问你一个子矩阵中蓝色连通块的个数 。

思路

大水题,我深度优先搜索判断联通块就完事了,然鹅一看数据,顿时倒地不起(救命!)。

回归正题,仔细一瞅发现一个奇怪的地方:保证所有蓝色格子之间只有一条路。也就是说,所有的联通块都是一棵树。这就见鬼了,树跟联通块有半毛钱关系啊!

按照大神的套路(宝宝你肯定看题解了对不对?)来做一下。

下面是样例一的图(太丑了\(233\)):

以第一个询问为例子,我们把所有的蓝点\(1\)连成树(相邻的两个\(1\)连边)。

发现联通块的数量\(=\)点的数量\(-\)边的数量,于是这道题变成了:给你一个\(01\)矩阵,求这个矩阵的一个子矩阵中的\(1\)的数量-连续两个\(1\)的数量。

由于数据范围死大,于是二维前缀和。对于连续两个\(1\)就可以发现一个1在向它周围判断即可

Code

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
int f[2001][2001],h[2001][2001],g[2001][2001];
char s[2001][2001];
int main()
{
    cin>>n>>m>>q;
    for(int i=1; i<=n; i++)
        cin>>s[i]+1;
    memset(f,0,sizeof(f));
    memset(h,0,sizeof(h));
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
        {
            f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+(s[i][j]-'0');
            h[i][j]=h[i-1][j]+h[i][j-1]-h[i-1][j-1];
            g[i][j]=g[i-1][j]+g[i][j-1]-g[i-1][j-1];
            if(s[i][j]=='1')
            {
                if(s[i][j-1]=='1') h[i][j]++;
                if(s[i-1][j]=='1') g[i][j]++;
            }
        }
    while(q--)
    {
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        int k1=f[x2][y2]-f[x2][y1-1]-f[x1-1][y2]+f[x1-1][y1-1];
        int k2=h[x2][y2]-h[x2][y1]-h[x1-1][y2]+h[x1-1][y1];
        int k3=g[x2][y2]-g[x1][y2]-g[x2][y1-1]+g[x1][y1-1];
        cout<<k1-k2-k3<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/lyfoi/p/9485062.html

时间: 2024-11-29 09:12:20

前缀和,二维前缀和!的相关文章

openjudge1768 最大子矩阵[二维前缀和or递推|DP]

总时间限制:  1000ms 内存限制:  65536kB 描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如,如下4 * 4的矩阵 0 -2 -7 09 2 -6 2-4 1 -4 1-1 8 0 -2 的最大子矩阵是 9 2-4 1-1 8 这个子矩阵的大小是15. 输入 输入是一个N * N的矩阵.输入的第一行给出N (0 < N <= 100).再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给

Good Bye 2015 C. New Year and Domino 二维前缀

C. New Year and Domino They say "years are like dominoes, tumbling one after the other". But would a year fit into a grid? I don't think so. Limak is a little polar bear who loves to play. He has recently got a rectangular grid with h rows and w

计蒜客模拟赛D1T1 蒜头君打地鼠:矩阵旋转+二维前缀和

题目链接:https://nanti.jisuanke.com/t/16445 题意: 给你一个n*n大小的01矩阵,和一个k*k大小的锤子,锤子只能斜着砸,问只砸一次最多能砸到多少个1. 题解: 将原矩阵顺时针旋转45°,二维前缀和预处理,然后枚举每一个可能砸到的正方形之和并取最大. 注:枚举的正方形的四个顶点必须是从原矩阵璇转过来的点,代码中用vis数组判断. #include <iostream> #include <stdio.h> #include <string.

弱校联盟10.7 I. Special Squares(二维前缀和)

题目链接: I. Special Squares There are some points and lines parellel to x-axis or y-axis on the plane. If arbitrary chosen two lines parallel to x-axis and two lines parallel to y-axis, one rectangle, or sometimes a square, will be formed. If a square i

CDOJ 1256 二维前缀和处理

昊昊喜欢运动 他NN 天内会参加MM 种运动(每种运动用一个[1,m][1,m] 的整数表示) 舍友有QQ 个问题 问昊昊第ll 天到第rr 天参加了多少种不同的运动 Input 输入两个数NN , MM (1≤N≤20001≤N≤2000 , 1≤M≤1001≤M≤100 ); 输入NN 个数aiai 表示在第i天昊昊做了第aiai 类型的运动; 输入一个数QQ (1≤Q≤1061≤Q≤106 ); 输入QQ 行 每行两个数 ll , rr (1≤l≤r≤n1≤l≤r≤n ); Output

二维前缀和 - 算法学习 - 输入输出优化

2017-08-27 11:11:38 writer:pprp 二维前缀和主要用到了容斥定理,具体实现还是有点复杂的 详见代码: /* @theme:二维前缀和 @writer:pprp @declare:用到容斥定理 @date:2017/8/27 */ #include <bits/stdc++.h> using namespace std; const int maxn = 1010; int n, m, a[maxn][maxn]; //输入优化 inline int read() {

二维前缀和

一维前缀和 : 这个优化 , 可以在 O (1) 的时间内计算出一个序列的和 , 二维前缀和 : 对于一个矩阵 , 也可以在 O (1) 的时间内计算出矩阵 (x1~x2)( y1 ~ y2 ) 的和 . sum[ i ] [ j ] 表示矩阵 1 ~ i , 1 ~ j 的和 , 那么由容斥原理知 sum[ 0 ] [ j ] 和 sum [ i ] [ 0 ] 均为 0 . 则  s[ x1 ~ x2 ] [ y1 ~ y2 ] = sum[ x2 , y2 ] + sum [ x1 - 1

杭电2018多校第四场(2018 Multi-University Training Contest 4) 1005.Problem E. Matrix from Arrays (HDU6336) -子矩阵求和-规律+二维前缀和

6336.Problem E. Matrix from Arrays 不想解释了,直接官方题解: 队友写了博客,我是水的他的代码 ------>HDU 6336 子矩阵求和 至于为什么是4倍的,因为这个矩阵是左上半边有数,所以开4倍才能保证求的矩阵区域里面有数,就是图上的红色阴影部分,蓝色为待求解矩阵. 其他的就是容斥原理用一下,其他的就没什么了. 代码: 1 //1005-6336-矩阵求和-二维前缀和+容斥-预处理O(1)查询输出 2 #include<iostream> 3 #in

切蛋糕(二分)(二维前缀和)

观察数据范围,n*m比较小,所以我们预先处理出前缀和. 然后我们可以考虑写一个函数来计算二维前缀和(二维前缀和大家都会的吧qwq,那我就不说了,就是要注意一下哪个是横轴哪个是纵轴) 之后就是二维上的二分位置,然后check看看符不符合二分出来的ans. 注:一半求最大化最小值和最小化最大值的问题,都可以往二分想.我们可以把最优化问题二分后来check转化为判定问题. #include<cstdio> #include<cstring> #include<iostream>