【洛谷】5026、Lycanthropy 落水后水的高度

【洛谷】5026、Lycanthropy 落水后水的高度


一、等差数列差分

1.1 等差数列差分

// java
// 一群人落水后求每个位置的水位高度
// 问题描述比较复杂,见测试链接
// 测试链接 : https://www.luogu.com.cn/problem/P5026
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Main {

	// 题目说了m <= 10^6,代表湖泊宽度
	// 这就是MAXN的含义,湖泊最大宽度的极限
	public static int MAXN = 1000001;

	// 数值保护,因为题目中v最大是10000
	// 所以左侧影响最远的位置到达了x - 3 * v + 1
	// 所以右侧影响最远的位置到达了x + 3 * v - 1
	// x如果是正式的位置(1~m),那么左、右侧可能超过正式位置差不多30000的规模
	// 这就是OFFSET的含义
	public static int OFFSET = 30001;

	// 湖泊宽度是MAXN,是正式位置的范围
	// 左、右侧可能超过正式位置差不多OFFSET的规模
	// 所以准备一个长度为OFFSET + MAXN + OFFSET的数组
	// 这样一来,左侧影响最远的位置...右侧影响最远的位置,
	// 都可以被arr中的下标表示出来,就省去了很多越界讨论
	// 详细解释看set方法的注释
	public static int[] arr = new int[OFFSET + MAXN + OFFSET];

	public static int n, m;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			// n有多少个人落水,每个人落水意味着四个等差数列操作
			n = (int) in.nval;
			in.nextToken();
			// 一共有多少位置,1~m个位置,最终要打印每个位置的水位
			m = (int) in.nval;
			for (int i = 0, v, x; i < n; i++) {
				in.nextToken(); v = (int) in.nval;
				in.nextToken(); x = (int) in.nval;
				// v体积的朋友,在x处落水,修改差分数组
				fall(v, x);
			}
			// 生成最终的水位数组
			build();
			// 开始收集答案
			// 0...OFFSET这些位置是辅助位置,为了防止越界设计的
			// 从OFFSET+1开始往下数m个,才是正式的位置1...m
			// 打印这些位置,就是返回正式位置1...m的水位
			int start = OFFSET + 1;
			out.print(arr[start++]);
			for (int i = 2; i <= m; i++) {
				out.print(" " + arr[start++]);
			}
			out.println();
		}
		out.flush();
		out.close();
		br.close();
	}

	public static void fall(int v, int x) {
		set(x - 3 * v + 1, x - 2 * v, 1, v, 1);
		set(x - 2 * v + 1, x, v - 1, -v, -1);
		set(x + 1, x + 2 * v, -v + 1, v, 1);
		set(x + 2 * v + 1, x + 3 * v - 1, v - 1, 1, -1);
	}

	public static void set(int l, int r, int s, int e, int d) {
		// 为了防止x - 3 * v + 1出现负数下标,进而有很多很烦的边界讨论
		// 所以任何位置,都加上一个较大的数字(OFFSET)
		// 这样一来,所有下标就都在0以上了,省去了大量边界讨论
		// 这就是为什么arr在初始化的时候要准备OFFSET + MAXN + OFFSET这么多的空间
		arr[l + OFFSET] += s;
		arr[l + 1 + OFFSET] += d - s;
		arr[r + 1 + OFFSET] -= d + e;
		arr[r + 2 + OFFSET] += e;
	}

	public static void build() {
	    // 从1到m+OFFSET的范围, 其实多求了一些数, 但无所谓, 这样是害怕漏了.
	    // 最终打印结果时, 只从 OFFSET+1 向后数m个数即可
		for (int i = 1; i <= m + OFFSET; i++) {
			arr[i] += arr[i - 1];
		}
		for (int i = 1; i <= m + OFFSET; i++) {
			arr[i] += arr[i - 1];
		}
	}
}

左神 视频 等差数列差分

二、多语言解法

C p p / G o / P y t h o n / R u s t / J s / T s Cpp/Go/Python/Rust/Js/Ts Cpp/Go/Python/Rust/Js/Ts

// cpp
// go 同上
# python
// rust
// js
// ts
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值