目录
前缀和模板
1、一维前缀和
import java.util.*;
class Main
{
static int N=100010;
static int[] a=new int[N],s=new int[N];
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),t=sc.nextInt();
for(int i=1;i<=n;i++)
{
a[i]=sc.nextInt();
s[i]=s[i-1]+a[i];
}
while(t-->0)
{
int l=sc.nextInt(),r=sc.nextInt();
System.out.println(s[r]-s[l-1]);
}
}
}
2、二维前缀和
import java.util.*;
class Main
{
static int N=1010;
static int[][] a=new int[N][N],s=new int[N][N];
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt(),q=sc.nextInt();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
a[i][j]=sc.nextInt();
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
while(q-->0)
{
int x1=sc.nextInt(),y1=sc.nextInt(),x2=sc.nextInt(),y2=sc.nextInt();
int res=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
System.out.println(res);
}
}
}
!3956. 截断数组 - 前缀和+枚举
题目:
一个数组切两刀,有三段,每段总和相等,问有多少种切法?
思路:
三段相等,则每段是sum/3,记作avg
如果总和sum不是avg的倍数,则不可能被平均分为3段
剩下的情况就是必然能分成3段,每段总和相等:
我们可以枚举第二刀的位置,枚举每种第二刀位置j时,对应有cnt种第一刀的切法
res累加这些情况即可
- 因为要分成3段,则第二刀之前至少要留2个元素给第一段和第二段,所以枚举第二段的位置i从3开始
- 先记录第一段的和==avg的个数
- 如果此时对应的第三段和==avg,则res累加
import java.util.*;
public class Main {
static int N = 100010;
static int[] s = new int[N];
static int n,m;
public static void main(String[] sss) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i=1;i<=n;i++)
{
s[i]=sc.nextInt();
s[i]+=s[i-1];
}
if(s[n]%3!=0) {
System.out.println(0);
return ;
}
int avg=s[n]/3;//均值
long res=0;
long cnt=0;//记录第一刀的位置
for(int i=3;i<=n;i++) //只要把第一段和第三段确定下来 就完成了题目
{
if(s[i-2]==avg) cnt++; //满足第一段==avg的个数 也就是合法的第一刀位置的个数
if(s[n]-s[i-1]==avg) res+=cnt; //如果第三段和==avg 说明这个第二刀可以对应之前所有出现过的合法的第一刀
}
System.out.println(res);
}
}
1230. K倍区间 - 前缀和 + 数学
题目:
思路:
- 要求有多少个子序列之和是k的倍数
- 则就是求满足(sum[r]-sum[l-1])%k==0的个数
- 也就是sum[r]%k==sum[l-1]%k
- 从前往后枚举,如果sum[i]%k=m,在i之前的sum[j]%k==m,说明(sum[i]-sum[j])%k==0,也就是aj+1……ai这一段是k倍区间,可以记入答案
- 所以对于每一个,只要统计它前面有多少个j就可以了
- 开一个cnt[],cnt[i]记录余数为i的前缀和有多少个
- 特殊情况:假如某段之和本来就是k倍区间,不需要减去区间,但cnt[0]保存的是i之前余数为0的前缀和的个数,这个区间本身没有被算进去,所以初始状态下,cnt[0]应该赋值为1
import java.util.*;
class Main
{
static int N=100010;
static long[] s=new long[N];
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),k=sc.nextInt();
long res=0;
long[] cnt=new long[N];
cnt[0]=1;
for(int i=1;i<=n;i++)
{
s[i]=sc.nextInt();
s[i]+=s[i-1];
}
for(int i=1;i<=n;i++)
{
res+=cnt[(int)(s[i]%k)]; //找前面有多少个余数相同的j
cnt[(int)(s[i]%k)]++; //当前这个i需要记录
}
System.out.print(res);
}
}
99. 激光炸弹 -
题目:
思路: