前缀和解决区间覆盖问题

159 阅读2分钟

题目描述

根据Sea Yellow,蓝色、白色、粉色的组合是最纯粹的! 现在有一个下标从1开始的数组a,长度为n,每个元素a都有一个额外的颜色属性。刚开始,所有的a0都是蓝色的,并且都等于0。然后,我们按顺序进行m次修改。每次修改会给出!.两个数,那么对于每个K==r,如果a而是蓝色的,那么它将变为白色,并+1;如果a是白色的,会变成粉色,并+1;如果ai是粉色的,会变成白色,并-2。请输出m次操作后的数组a(只需要输出数值,不需要输出颜色)。

输入:

第一行是整数n和m。1<=n,m<=1e5. 后面紧跟着m行,每行两个数,r,1<=<=r<=n。

输出:

输出一行n个整数。

示例 1:

输入: 
6 3
1 3
3 4
3 6

输出: 
1 1 0 2 1 1

解题思路

第一眼看到题目,一开始我用一个数组记录每次操作的颜色状态,根据颜色进行判断+1还是-2。后来看到题解,发现还可以用前缀和来解决问题。核心思想就是:数组a的颜色初始化是蓝色的,只要操作过一次就不会回去蓝色,只会在白色和粉色循环改变,因此可以利用被操作的次数的来+1或者-2。比如,下标i的元素,被操作4次,那么就会有4/2=2次是白色,也就是加了2次1,那么很自然,粉色次数 = 操作次数-白色的次数-蓝色次数。即4-2-1=1,需要减去1次2。所以问题就在如何求每个数组元素被操作的次数,这就是区间覆盖问题。

利用前缀和求区间被操作的次数:

维护一个数组v,遍历m次操作的左右区间[l,r],v[l]自增1,v[r+1]自减1。遍历m次后,对v进行前缀和,就可以得到前缀和数组,对于v[i],含义就是每个i下标的数组a被操作的次数。如此便得到了数组a每个元素被操作的次数。

需要注意的是,之所以是v[r+1]自减1,因为前面的v[l]已经加1了,超过r的元素都不会被操作,所以要抵消前面的v[l]自增的1。

代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[] v = new int[n+2];

        for (int i = 0; i < m; i++) {
            int l = scanner.nextInt();
            int r = scanner.nextInt();
            v[l]++;
            v[r+1]--;
        }
        int first = 0;
        for (int i = 1; i <= n; i++) {
            v[i] += v[i-1];
            first = v[i]==0?0:1;
            // first表示蓝色出现的次数
            System.out.print(first + v[i]/2 - (v[i]-v[i]/2-first)*2 + " ");
        }
    }
}