二阶差分及[绝世武功]

二阶差分可以看成是:差分数组+等差数列。之前学习的差分法的应用就是在区间内操作,单一的加减一个固定的数字。(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_{i}=a_{i}+s+(i-l)*d,i\in [l,r]

此时a数组对b数组的影响:

b_{l}=(a_{l}+s)-a_{l-1}=b_{l}+s

b_{i}=a_{i}+(i-l)*d-(a_{i-1}+(i-1-l)*d)=b_{i}+d,i\in [l+1,r]

b_{r+1}=a_{r+1}-(a_{r}+t)=b_{r+1}-t

此时b数组对c数组的影响:

c_{l}=b_{l}+s-b_{l-1}=c_{l}+s

c_{l+1}=(b_{l+1}+d)-(b_{l}+s)=c_{l}+d-s

c_{i}=(b_{i}+d)-(b_{i-1}+d)=c_{i},i\in[l+2,r]

c_{r+1}=(b_{r+1}-t)-(b_{r}+d)=c_{r+1}-d-t

c_{r+2}=b_{r+2}-(b_{r+1}-t)=c_{r+2}+t

不难看出每组(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);
	}
}


你可能感兴趣的:(学习记录,刷题,java)