二阶差分可以看成是:差分数组+等差数列。之前学习的差分法的应用就是在区间内操作,单一的加减一个固定的数字。(b[i]=a[i]-a[i-1],a[0]=0)然后前缀和就是所求的数组了。
但是遇到等差数列时就不能这样去解题了。比如对区间[2,6]加上一个首项为a,公差为d的等差数列,就需要对每一个点进行更改。
我们设a为原数组,b为一阶差分数组,c为二阶差分数组。当在[l,r]内加上一个首项是s,公差为d,末项为t=s+(r-l)*d的等差数列:
对a的影响:
此时a数组对b数组的影响:
此时b数组对c数组的影响:
不难看出每组(l,r,s,d)在c数组的影响只改变了四个值(l,l+1,r+1,r+2),然后再将c数组做两次前缀和就可以得到数组a。
当然,光看公式推导可能还没那么清除。可以自己画一个三行的表格。把原数组、一阶差分数组、二阶差分数组给罗列出来,再在上面进行区间加等差数列的操作。我就不画咯,自己画画吧,自己画画记得更清楚哦。
画出来可以很清楚的看到二阶数组的变化:
c[l]+=s;
c[l+1]+=d-s;
c[r+1]=c[r+1]-d-t;
c[r+2]=c[r+2]+t;
然后按照这样的变化吧c[]求出再复原就可以得到所求的新a[]了。
以下题为例进行学习:(思路没问题,写的几个个代码都可以跑出结果,但是前两个会超时;第三个是用的Java快读写的。)
小明在练习绝世武功, nn 个练功桩排成一排,一开始每个桩的损伤为 00。
接下来小明会练习 mm 种绝世武功,每种武功都会对 [l, r][l,r] 区间分别造成 [s,e][s,e] 的伤害。
这个伤害是一个等差序列。例如 l = 1, r = 4, s = 2, e = 8l=1,r=4,s=2,e=8 ,则会对 1-41−4 号练功桩造成2, 4, 6, 82,4,6,8 点损伤。
小明想让你统计一下所有练功桩的损伤的和。
输入描述
第一行输入 n, m,代表练功桩的数量和绝世武功的种类数。
接下来 m 行输入 4 个整数 l, r, s, e 。
1≤n≤10^7,1≤m≤3×10^5,1≤l,r≤n
输出描述
输出一个整数代表所有练功桩的损伤和, 题目保证所有输入输出都在 [0,9×10^18]
输入输出样例
示例 1
输入
6 2 1 5 2 10 2 4 1 1
输出
33
运行限制
- 最大运行时间:1s
- 最大运行内存: 512M
用Scanner可以跑出结果的,但是会超时哦:
import java.util.Scanner;
public class Main {
static int N=10000010;
static int sum;
static int[] a=new int[N];//原数组
static int[] b=new int[N];//一阶差分数组
static int[] c=new int[N];//二阶差分数组
public static void main(String[] args) {
int n,m,l,r,s,e,d,t;
Scanner in=new Scanner(System.in);
n=in.nextInt();
m=in.nextInt();
for (int i = 1; i <=m; i++) {
l=in.nextInt();
r=in.nextInt();
s=in.nextInt();
e=in.nextInt();
d=(e-s)/(r-l);
t=s+(r-l)*d;
c[l]=s;
c[l+1]=d-c[l];
c[r+1]=c[r+1]-d-t;
c[r+2]=c[r+2]+t;
}
for (int i = 1; i <=n; i++) {
b[i]=b[i-1]+c[i];
a[i]=a[i-1]+b[i];
sum+=a[i];
}
System.out.println(sum);
}
}
想着Scanner超时那我用比他快的BufferedReader不就好了嘛。然鹅.....思想很美好,现实很残酷。跪了呀。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main{
static int N=10000010;
static int sum;
static int n,m,l,r,s,e,d,t;
static int[] a=new int[N];//原数组
static int[] b=new int[N];//一阶差分数组
static int[] c=new int[N];//二阶差分数组
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
String[] num=bf.readLine().split(" ");
n=Integer.parseInt(num[0]);
m=Integer.parseInt(num[1]);
for (int i = 1; i <=m; i++) {
String[] num2=bf.readLine().split(" ");
l=Integer.valueOf(num2[0]).intValue();
r=Integer.valueOf(num2[1]).intValue();
s=Integer.valueOf(num2[2]).intValue();
e=Integer.valueOf(num2[3]).intValue();
d=(e-s)/(r-l);
t=s+(r-l)*d;
c[l]=s;
c[l+1]=d-c[l];
c[r+1]=c[r+1]-d-t;
c[r+2]=c[r+2]+t;
}
for (int i = 1; i <=n; i++) {
b[i]=b[i-1]+c[i];
a[i]=a[i-1]+b[i];
sum+=a[i];
}
System.out.println(sum);
}
}
这个用的java快读,就是StreamTokenize。可以去看一下这篇博客写的很清楚也很好懂。下面放代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class LQ1368_2{
static int N=10000010;
static int sum;
static int n,m,l,r,s,e,d,t;
static int[] a=new int[N];//原数组
static int[] b=new int[N];//一阶差分数组
static int[] c=new int[N];//二阶差分数组
//static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static void main(String[] args) throws IOException {
in.nextToken();
n=(int)in.nval;
in.nextToken();
m=(int)in.nval;
for (int i = 1; i <=m; i++) {
in.nextToken();
l=(int)in.nval;
in.nextToken();
r=(int)in.nval;
in.nextToken();
s=(int)in.nval;
in.nextToken();
e=(int)in.nval;
d=(e-s)/(r-l);
t=s+(r-l)*d;
c[l]+=s;
c[l+1]+=d-s;
c[r+1]=c[r+1]-d-t;
c[r+2]=c[r+2]+t;
}
for (int i = 1; i <=n; i++) {
b[i]=b[i-1]+c[i];
a[i]=a[i-1]+b[i];
sum+=a[i];
}
System.out.println(sum);
}
}