直接贴道模板题
AcWing 795. 前缀和
题意:求出序列第l位至第r位的和
前缀和也是一种避免遍历造成时间浪费的方法,可以用O(1)的时间求出一个区间和,在构造序列时可以顺便构造前缀和序列,后续直接使用,实现意义即是前缀和序列的第 i 位的值等于原序列前 i 位的和,特殊题型中可能会有变化,不过大体思路不变
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;
import java.util.Vector;
public class Main {
static Scanner tab = new Scanner(System.in);
static BufferedWriter tabb = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 110000;
static int n;
static int a[]=new int [N];
static int b[]=new int [N];
public static void main(String[] args) throws IOException {
n=tab.nextInt();
int m=tab.nextInt();
for(int i=1;i<=n;i++) {
a[i]=tab.nextInt();
b[i]=a[i]+b[i-1];
}
while(m-->0) {
int l=tab.nextInt();
int r=tab.nextInt();
System.out.println(b[r]-b[l]+a[l]);
}
}
}
AcWing 796. 子矩阵的和
题意:给出矩阵,求出两个坐标构成的矩形内值的和
从这题开始遇到的都是二维前缀和,和一维差不多,不过由于是二维,无论是构造还是计算中都要考虑到重复计算的问题,类似高中的集合知识,计算两个集合的并集时,要用两个集合相加并减去交集部分,反之减去并集的话也要加上交集的部分
此题中可以用右下坐标的前缀和减去上和左两方向的多余值,由于重复减去了左上值两次,最后再加上一个左上的前缀和值
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;
import java.util.Vector;
public class Main {
static Scanner tab = new Scanner(System.in);
static BufferedWriter tabb = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 1100;
static int a[][]=new int [N][N];
static int b[][]=new int [N][N];
public static void main(String[] args) throws IOException {
int n=tab.nextInt();
int m=tab.nextInt();
int q=tab.nextInt();
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
a[i][j]=tab.nextInt();
b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j];
}
}
while(q-->0) {
int x1=tab.nextInt();
int y1=tab.nextInt();
int x2=tab.nextInt();
int y2=tab.nextInt();
int sum=b[x2][y2]-b[x2][y1-1]-b[x1-1][y2]+b[x1-1][y1-1];
System.out.println(sum);
}
}
}
AcWing 99. 激光炸弹
题意:给出目标在矩阵中的坐标及权值,给出炸弹波及范围,求出炸弹能够覆盖位置权值的最大值
二维区间和问题,很直观的联系到前缀和。构造前缀和序列,以炸弹范围r为矩阵边长求区间和即可。需要注意的是题中默认是将目标放在矩阵的交点上而并非是格中,可以将坐标整体+1,既能实现目标从点转移至格,同时也方便后续前缀和序列的边界处理。
import java.io.*;
import java.util.*;
public class Main {
static Scanner tab = new Scanner(System.in);
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 5010;
static int s[][]=new int [N][N];
public static void main(String[] args) throws IOException {
int n,m;//定义矩形长宽
int cnt=tab.nextInt();
int r=tab.nextInt();
r=Math.min(r, 5001);
n=r;
m=r;
while(cnt-->0) {
int x=tab.nextInt();
int y=tab.nextInt();
int w=tab.nextInt();
x++;//实现目标从点转移到格位,同时初始位置设为1简化前缀和的构造
y++;
s[x][y]+=w;
n=Math.max(n, x);
m=Math.max(m, y);
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
}
int res=0;
for(int i=r;i<=n;i++) {
for(int j=r;j<=m;j++) {
res=Math.max(res, s[i][j]-s[i-r][j]-s[i][j-r]+s[i - r][j - r]);
}
}
System.out.println(res);
}
}
注意的是二维数组如果开的过大的话会爆掉,最好定义为全局变量,c或c+同理
AcWing 1230. K倍区间
题意:给出序列,求出序列中区间和是k的倍数的区间有多少个
首先区间和肯定会用到前缀和思想,实现前缀和后如果暴力枚举还是会超时,于是分析一下题意,可以得出以第 i 位为右端点的前缀和Si左侧共有(0~i-1)i 种左端点t,每计算一个以Si为右端点的区间值即是Si减去St的值是否为k的倍数,换句话来说就是Si mod k = St mod k 即是此区间符合条件。
这里可以使用类似递推的思想,从1至n枚举右端点,建立一个cnt数组,将下标m定义为mod k,来记录mod k = m的区间数。由于右端点从左至右枚举,cnt数组会随着计算实时变化,后续计算的区间也不会发生遗漏。
import java.io.*;
import java.util.*;
public class Main {
static Scanner tab = new Scanner(System.in);
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 100010;
public static void main(String[] args) throws IOException {
int n=tab.nextInt();
int k=tab.nextInt();
long a[]=new long [N];//int会爆
long cnt[]=new long [N];//记录mod k区间数
for(int i=1;i<=n;i++) {
//构造前缀和
a[i]=tab.nextLong();
a[i]=a[i-1]+a[i];
}
long res=0;
cnt[0]=1;//初始化
for(int i=1;i<=n;i++) {
res+=cnt[(int)(a[i]%k)];
cnt[(int)(a[i]%k)]++;
//核心代码
//一定要首先计算数量,再将cnt更新
//如果先更新cnt会出现一个Si-Si的结果
//而前缀和不应存在这种情况
}
System.out.println(res);
}
}
前缀和题量不多 后续再补
打团队赛都凑不出队友来 真是可悲