MENU

Hands that shed innocent blood

March 23, 2019 • Read: 3514 • 算法阅读设置

最容易想到的思路就是从左往右遍历,遍历的过程中,如果当前剑长不等于 0,就从当前位置往左,把剑长范围内的人都 + 1,表示被砍到 1 次。时间复杂度 $O (n^2)$

上面的解法很明显会超时,正确解法应该是线段树,或者差分加前缀和

如果用线段树做就是裸题,没什么好说的,这里说一下用差分加前缀和的做法

首先定义差分数组 $d$,初始值全部为 0,差分的性质是可以将区间修改改为单点修改,例如要将 $[L,R]$ 区间内所有值加上 $C$,相当于将差分数组中 $d [L]+=C$ 以及 $d [R+1]-=C$。差分还有一个性质是:差分序列的前缀和等于原始数组,利用这两个性质,我们就可以快速统计出整个区间每个人被砍过的次数

具体做法是,遍历剑长数组,如果不为 0,就将 $d [max (0,i - arr [i])]+=1$ 以及 $d [max (0, i - 1) + 1] -= 1$

  • import java.util.Scanner;
  • public class Main {
  • public static void main(String[] args) {
  • Scanner cin = new Scanner(System.in);
  • int n = cin.nextInt();
  • long[] arr = new long[n]; // 原始数组
  • long[] d = new long[n]; // 差分序列
  • long[] life = new long[n]; // 生命值
  • for (int i = 0; i < n; i++) {
  • arr[i] = cin.nextInt();
  • if (arr[i] != 0) { // 剑长不等于0才修改差分序列
  • d[(int) Math.max(0, i - arr[i])] += 1; // d[L] += 1
  • d[Math.max(i - 1, 0) + 1] -= 1; // d[R + 1] -= 1
  • }
  • }
  • for (int i = 0; i < n; i++)
  • life[i] = cin.nextInt();
  • long ans = d[0] >= life[0] ? 1 : 0; // 因为差分序列的第一个值就是原始序列,所以第一个值直接结算
  • for (int i = 1; i < n - 1; i++) { // 最后一个人不用考虑,因为没有人砍他
  • d[i] += d[i - 1]; // 前缀和
  • ans += (d[i] > life[i] ? 1 : 0);
  • }
  • System.out.println(n - ans);
  • }
  • }
Last Modified: March 29, 2019
Archives Tip
QR Code for this page
Tipping QR Code