首页 > 试题广场 >

小红的区间构造

[编程题]小红的区间构造
  • 热度指数:36 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}在本题中我们认为区间的左右端点必须是整数。
小红拿到了一个长为 n 的数组 a=\{a_1,a_2,\dots,a_n\},其中  表示恰好有  个区间包含 
请你构造 m 个区间 \left[l_i, r_i \right] \left(1\leqq l_i \leqq r_i \leqq n\right),恰好满足数组  的限制条件。

输入描述:
第一行输入两个正整数 
第二行输入  个整数 a_i\left(0\leqq a_i \leqq 2\times10^5 \right)


输出描述:
如果不存在合法的 m 个区间,请输出 ,否则对于构造的每个区间,新起一行依次输出 
\hspace{15pt}如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
示例1

输入

5 5
1 2 3 4 5

输出

1 5
2 5
3 5
4 5
5 5
示例2

输入

5 2
1 2 1 2 1

输出

-1
// 一个区间对于某个数i最多贡献1个
// 差分思想:枚举a[i], 计算a[i]-a[i-1]
//                    少的部分即一些区间的开头L为i
//                    多的部分即一些区间的结尾R为i-1
//                    L、R的个数是一样的, 这样计算出来的是最少的区间个数min
//                    最多的区间个数max=sum(a[i]), 全是1个点的区间覆盖全部a[i]
//
// m ∈ [min,max]有解
// 具体构造:可以从L、R任意选一对出来, 若对数不够m个, 则在区间[l,r]内再拆分(最简单的是拆为单个点)
//
//  1  5  2       max = 1 + 5 + 2 = 8
// +1 +4 -3 -2    min = +1 +4 = -(-3 -2) = 5
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] sp = br.readLine().split(" ");
        int n = Integer.parseInt(sp[0]), m = Integer.parseInt(sp[1]);
        int[] a = new int[n + 2];
        long min = 0, max = 0; // 和可能很大, 先用Long计算, L、R后面再弄
        sp = br.readLine().split(" ");
        for (int i = 1; i <= n; i++) {
            a[i] = Integer.parseInt(sp[i-1]);
            min += Math.max(0, a[i] - a[i - 1]);
            max += a[i];
        }

        if (m < min || m > max) { // m ∈ [min,max] 
            System.out.println(-1);
            return;
        }

        List<Integer> L = new ArrayList<>();
        List<Integer> R = new ArrayList<>();
        for (int i = 1; i <= n + 1; i++) { // a[0]=a[n+1]=0:处理开头结尾
            int d = a[i] - a[i - 1];
            if (d > 0) {
                while (d-- > 0) {
                    L.add(i);
                }
            } else {
                d = -d;
                while (d-- > 0) {
                    R.add(i - 1); // 结尾不包含i
                }
            }
        }

        int rest = m - (int)min; // 要额外凑的
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < min; i++) {
            int l = L.get(i), r = R.get(i);
            // 该区间[l,r]可以拆为r-l+1个点, 最多额外提供r-l个, 扣除区间本身1个🔥
            int d = Math.min(rest, r - l); 
            rest -= d; 
            for (int j = l; j < l + d; j++) { // 1个点的区间:[j,j]
                sb.append(j).append(' ').append(j).append('\n'); 
            }
            sb.append(l + d).append(' ').append(r).append('\n'); // 剩余:[l+d,r]
        }
        System.out.print(sb);
    }
}


发表于 2025-12-20 18:39:56 回复(0)