1.前缀差分
一维前缀和
原数组a[a1,a2,…an]
前缀和:Si=a1+a2+a3+…+ai(其实是一个公式),必须让下标从1开始,前i个数的和
Si是进行for循环,快速求出一个数组中一段数的和S[r]-s[l-1]求出l到r的和
s[0]=0;
for(int i=1;i<n;i++)
{
s[i]=s[i-1]+a[i];
}
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
import java.util.Scanner;
public class test01 {
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
System.out.println("输入数组大小");
int n=in.nextInt();//数组的长度
int[] arr = new int[n+1];
//输入数组元素
for (int i=1;i<=n;i++){
System.out.println("数组元素"+i);
arr[i]=in.nextInt();
}
int[] s= new int[n+1];
s[0]=0;
for(int i=1;i<=n;i++){
s[i]=s[i-1]+arr[i];
System.out.println(s[i]);
}
int l=in.nextInt();//这个是左边界
int r=in.nextInt();//这个是右边界
System.out.println(s[r]-s[l-1]);
}
}
二维前缀和
S【i】[j]就是i之前j之前的所有元素的和,是从左顶点a【0】【0】到a【i】【j】的一个矩形和
Sx2,y2-Sx2,y1-1-Sx-1,y2+Sx-1,y-1 求两点之间的和
Sij=si-1,j+si,j-1-si-1,j-1+aij
package acwing基础算法;
这里就是(2,2)和(3,3),因为我们要看的是(2,2)位置的这个元素和(3,3)位置这个元素之间的部分和
这是个元素位置,不是一个点,这里的y1-1,x-1也是因为我们要减去的是(3,1)和(1,3)位置的元素,最终加的是(1,1)
其实归根到底的原因是因为这个地方的(2,2),如果这里按得是(1,1)的话就不需要进行-1的操作,这里可以这样简单记忆,就是只要是关于x1,y1的相关操作都去-1
import java.io.BufferedInputStream;
import java.util.Scanner;
public class PreMultiSum {
private static final int N = 1010;
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
//n行
int n = in.nextInt();
//m列
int m = in.nextInt();
int q = in.nextInt();
int [][]a = new int[N][N];
int [][]s = new int[N][N];
//二维数组赋值
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
a[i][j] = in.nextInt();
}
//求完整和
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]+a[i][j];
}
//求部分和
while(q--!=0){
int x1=in.nextInt(),y1=in.nextInt(),x2=in.nextInt(),y2=in.nextInt();
System.out.println(s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
}
}
}
差分:
a1,a2,…an
构造b1,b2…bn使得ai=b1+b2+…+bi
差分:只要有b数组可以在O(n)的时间内得到a数组,b的前缀和就是a数组
如果我们想要ai+c,所有的ai都加一个c,需要O(n)遍历一遍
但是如果有了b数组,只需要在b1+c,所有的ai都包含b1,b1加c,其他的ai也一定会加c
想要在某一个范围[l,r]内+c,其余地方不加c的话,只需要bl+c,br+1 -c ,c一加一减局可以抵消掉
因为b数组的存在,这种操作的时间复杂度就是O(1)
输入一个长度为n的整数序列
接下来输入m个操作,每个操作包含三个整数l,r,c,表示将序列中[l,r]之间的每个数加上c
输入:整数n--数组个数
整数m--m次操作
整数l--从l开始
整数r--到r结束
整数c--添加的元素
假定初始的时候a数组全部为0,那么b数组也全部为0,假定是进行n次插入操作
在原始的【1,1】区间加上a1,在原始的【2,2】区间加上a2,。。。
import java.io.BufferedInputStream;
import java.util.Scanner;
public class test02 {
private static final int N=100010;
public static void insert(int l,int r,int x,int []b){
b[l]+=x;//a=1 2 2 1 2 1 insert(i,i,a[i],b); 1===n
b[r+1]-=x;//b1=1 b2=-1 b2=-1+2=1 b3=-2 b3=-2+2=0 b4=-2 b4=-2+1=-1 b5=-1 b5=-1+2=1 b6=-2 b6=-1 b7=-1
}//b1=1,b2=1,b3=0,b4=-1,b5=1,b6=-1,b7=-1
//插入之后又减去相当于没插入,但是累加的时候可以获得最后一个
//a2是b1+b2的和,b1添加的a1在b1加上又在b2减去所以相当于没加,a2添加在b2上正好累加,(求a3的时候又会将a2减去正合适)
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
int n = in.nextInt(),m = in.nextInt();
int[] a = new int[N];
int[] b = new int[N];
//给a数组赋值
for(int i=1;i<=n;i++){
a[i]=in.nextInt();
}
//按a数组构造b数组
for(int i=1;i<=n;i++){
insert(i,i,a[i],b);
}
//多次输入,给出每次修改的差分数列后的结果
while(m--!=0){
int l=in.nextInt(),r=in.nextInt(),c=in.nextInt();
insert(l,r,c,b);
}
//重新求和数列同时注意b[0]等于0
for(int i=1;i<=n;i++){
b[i]+=b[i-1];
}
for(int i=1;i<=n;i++){
System.out.print(b[i]+" ");
}
}
}
二维差分
差分矩阵bij的构造:
满足原矩阵是差分矩阵的和就可以了
bx1,y1 +=c//将点(x,y)下的所有的点都加上c了肯定要减去
bx2+1,y1 -=c//将最下面的线往下减去c
bx1,y2+1-=c//将最右边的下往下减去c
bx2+1,y2+1+=c;//因为减去了两次所以要再加上
import javafx.scene.transform.Scale;
import java.io.BufferedInputStream;
import java.util.Scanner;
public class test03 {
private static final int N=100010;
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
int n=in.nextInt();
int m=in.nextInt();
int q=in.nextInt();
int[][] a = new int[N][N];
int[][] b=new int[N][N];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++){
a[i][j]=in.nextInt();
}
}
//把a矩阵的每个数插入空的b中去,和一维的是差不多的操作,插入到b中,形成差分矩阵
for(int i=1;i<=n;i++) {
for (int j = 1; j <= m; j++) {
insert(i,j,i,j,a[i][j],b);
}
}
while(q--!=0){
int x1=in.nextInt();
int y1=in.nextInt();
int x2=in.nextInt();
int y2=in.nextInt();
int c=in.nextInt();
insert(x1,y1,x2,y2,c,b);
}
for(int i=1;i<=n;i++) {
for (int j = 1; j <= m; j++) {
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
System.out.println(b[i][j]);
}
}
}
public static void insert(int x1,int y1,int x2, int y2 ,int c,int[][] b){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
}
超时了,我看的下面这个链接的操作,输入流才不超时的
https://www.cnblogs.com/mingustc/p/14619442.html
贴一下大佬的代码
package acwing基础算法;
import java.io.*;
import java.util.Scanner;
public class DifferenceMulti {
private static final int N=1010;
public static void insert(int x1,int y1,int x2,int y2,int c,int [][]b){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
String[] s1 = reader.readLine().split(" "); //将读入的字符串按照“ ”空格进行分割,获取一个个单个的字符
int n = Integer.parseInt(s1[0]),m = Integer.parseInt(s1[1]);//将字符强制转换为int型数据
int q = Integer.parseInt(s1[2]);
int [][]a = new int[N][N];
int [][]b = new int[N][N];
for(int i=1;i<=n;i++) {
String[] s2 = reader.readLine().split(" ");
for (int j = 1; j <= m; j++) {
a[i][j] = Integer.parseInt(s2[j - 1]);
insert(i, j, i, j, a[i][j], b);
}
}
while(q--!=0){
String[] s3 = reader.readLine().split(" ");
int x1 = Integer.parseInt(s3[0]),
y1 = Integer.parseInt(s3[1]),
x2 = Integer.parseInt(s3[2]),
y2 = Integer.parseInt(s3[3]),
c = Integer.parseInt(s3[4]);
insert(x1,y1,x2,y2,c,b);
}
for(int i=1;i<=n;i++) {
for (int j = 1; j <= m; j++) {
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
//输出
writer.write(b[i][j] + " ");
}
writer.write("\n");
}
writer.flush();
reader.close();
writer.close();
}
}