Codeforces Round 891 (Div. 3) 题解 A-E

目录

  • A - Array Coloring
  • B - Maximum Rounding
  • C - Assembly via Minimums
  • D - Strong Vertices
  • E - Power of Points

A - Array Coloring

原题链接

题目描述
给你一个由 n个整数组成的数组。确定是否有可能用两种颜色给数组中的所有元素着色,使得两种颜色元素的和具有相同的奇偶性,如果可以输出YES,否则输出NO

思路

  • 判断奇数的个数,如果是奇数个,那么永远无法满足题意,如果是偶数个,一定可以满足题意。
public static void solve() throws IOException {
    int n = readInt();
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        int a = readInt();
        if (a % 2 != 0) cnt++;
    }
    printWriter.println(cnt % 2 == 0 ? "YES" : "NO");
}

B - Maximum Rounding

原题链接

题目描述
给定一个自然数x,你可以从x的第k位进行四舍五入(最右边为第0位),四舍五入的规则如下:

  • 如果第(k−1)位上的数字大于或等于5,则第k位上的数字增加1,否则第k位上的数字保持不变
  • 如果在运算之前,第k位上的数字是9,而且它会被增加1,那么我们寻找最小的位置k′(k′>k),第k′位置的数字小于9,并在第k′位置的数字上加上1
  • 之后,所有位置小于 k的数字都被替换为 0

输出x的最大值

样例输入

12 //测试数据的个数
1
5
99
913
1980
20444
20445
60947
419860
40862016542130810467
45
445

样例输出

1
10
100
1000
2000
20444
21000
100000
420000
41000000000000000000
100
1000

思路
分类讨论

  • 如果这个数的最高位大于等于5,那么可以直接在最高位四舍五入(第八个样例)
  • 判断所有位是不是都小于5,如果是,那么无法四舍五入,输出原数(第六个样例)
  • 找第一个大于等于5的数的位置 i,那么 i以及比i低的位都将变为0,再看比i高的位看是否等于4,如果等于4说明比i更高的位+1后也可以进位(4+1->5,5进位->0第十二个样例),那么继续枚举,如果不等于4,那么低i+1位+1后,更高的位直接输出原数(第七个样例)
  • 特判进位后全为0的情况,第十一个样例
public static void solve() throws IOException {
    String s = readString();
    if (s.charAt(0) >= '5') {//第一位就大于5
        int len = s.length();
        printWriter.print("1");
        for (int i = 0; i < len; i++) {
            printWriter.print("0");
        }
        printWriter.println();
    } else {
        boolean f = false;//是否有大于5的数
        int idx = 0;//第一个大于等于5的位置
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) >= '5') {
                idx = i;
                f = true;
                break;
            }
        }
        if (!f) {//全小于5
            printWriter.println(s);
        } else {
            StringBuilder sb = new StringBuilder();
            for (int i = s.length() - 1; i >= idx; i--) {
                sb.append("0");//当前位和更低位变为0
            }
            boolean flag = false;
            for (int i = idx - 1; i >= 0; i--) {
                if (flag) {//更高的位都无法进位了
                    int p = s.charAt(i) - '0';
                    sb.append(String.valueOf(p));
                    continue;
                }
                if (s.charAt(i) == '4') {
                    sb.append("0");//是4,继续枚举更高位
                } else {
                    flag = true;//更高的位都无法进位了
                    int p = s.charAt(i) - '0' + 1;
                    sb.append(String.valueOf(p));
                }
            }
            String t = sb.reverse().toString();
            if (t.charAt(0) == '0') {
                t = "1" + t;//特判进位后全为0的情况
            }
            printWriter.println(t);
        }
    }
}

C - Assembly via Minimums

原题链接

题目描述
萨沙有一个长度为n的整数数组a,他记录下了所有 i 、 j ( i < j ) i、j(iiji<j的最小值 a i a_i ai a j a_j aj后组成了一个长度为 n ∗ ( n − 1 ) 2 \frac {n * (n - 1)} {2} 2n(n1)的新数组b(数组b是的顺序是随机的,也就是说你可以以任意顺序返回数组a)。还原数组a

样例输入

5 //测试数据的个数
3
1 3 1
2
10
4
7 5 3 5 3 3
5
2 2 2 2 2 2 2 2 2 2
5
3 0 0 -2 0 -2 0 0 -2 -2

样例输出

1 3 3
10 10
7 5 3 12
2 2 2 2 2
0 -2 0 3 5

思路

  • 将数组b从小到大排序,统计每个数字出现的次数,如果剩余出现的次数大于等于还需要比较的数的个数,那么这个数就是数组a的元素之一
public static void solve() throws IOException {
   int n = readInt();
    Map<Integer, Integer> map = new HashMap<>();
    int q = n * (n - 1) / 2;//数组b的长度
    int[] arr = Utils.nextIntArray(q);

    Arrays.sort(arr, 1 , q + 1);
    for (int i = 1; i <= q; i++) {
        map.put(arr[i], map.getOrDefault(arr[i], 0) + 1);
    }
    List<Integer> list = new ArrayList<>();
    int p = n;// 还需要比较的数的个数
    for (int i = 1; i <= q; i++) {
    	// 剩余出现的次数大于等于还需要比较的数的个数,那么这个数就是数组a的元素之一
        if (map.get(arr[i]) > 0 && map.get(arr[i]) >= (p - 1)) {
            list.add(arr[i]);
            map.put(arr[i], map.getOrDefault(arr[i], 0) - (p - 1));
            p--;
        }
    }
    //数组 a的长度还没有达到 q,那就用数组 b的最后一个数弥补
    for (int i = 0; i < p; i++) {
        list.add(arr[q]);
    }
    for (int i = 0; i < n; i++) {
        printWriter.print(list.get(i) + " ");
    }
    printWriter.println();
}

D - Strong Vertices

原题链接

题目描述
给定两个长度为n的数组ab,数组下标都是从 1 ∼ n 1 \sim n 1n。现在要求你构建一个有向图,如果 a u − a v ≥ b u − b v a_u - a_v \ge b_u - b_v auavbubv,那么就存在一条 u指向 v的边。如果一个点可以到达图中的任意一个点,那么这个点就是强顶点,求出所有强顶点的个数和编号

样例输入

5 //测试数据的个数
4
3 1 2 4
4 3 2 1
5
1 2 4 1 2
5 2 3 3 1
2
1 2
2 1
3
0 2 1
1 3 2
3
5 7 4
-2 -3 -6

样例输出

1
4 
2
3 5 
1
2 
3
1 2 3 
2
2 3 

思路

  • 公式转换:其中的 a u − a v ≥ b u − b v a_u - a_v \ge b_u - b_v auavbubv可以转换为 a u − b u ≥ a v − b v a_u - b_u \ge a_v - b_v aubuavbv,那么就相当于求 a i − b i a_i - b_i aibi的最大值,这样可以满足任意的 a j − b j ≤ a i − b i a_j - b_j \le a_i - b_i ajbjaibi,这样点i可以到达任意点j
public static void solve() throws IOException {
    int n = readInt();
    int[] arr1 = Utils.nextIntArray(n), arr2 = Utils.nextIntArray(n);
    long max = Long.MIN_VALUE;
    for (int i = 1; i <= n; i++) {
        max = Math.max(max, arr1[i] - arr2[i]);//求最大值
    }
    List<Integer> list = new ArrayList<>();
    for (int i = 1; i <= n; i++) {
        if (arr1[i] - arr2[i] == max){
            list.add(i);// 所有的强顶点
        }
    }
    printWriter.println(list.size());
    for (int p : list) {
        printWriter.print(p + " ");
    }
    printWriter.println();
}

E - Power of Points

原题链接

题目描述
给定一条数轴上的n个点,对于其中的每个点s,它将与所有点x(包括s自己)分别构成一条条线段,这条线段能访问到 x ∼ s ( x ≤ s ) x \sim s(x \le s) xs(xs)或者 s ∼ x ( s ≤ x ) s \sim x(s \le x) sx(sx)上的点,你需要求出数轴中的每个点作为s时,所有点被访问到的次数之和。

输入样例

3 //测试数据的个数
3
1 4 3
5
1 2 5 7 1
4
1 10 100 1000

输出样例

8 7 6
16 15 18 24 16
1111 1093 1093 2893

思路:排序+前缀和+数学

  • 先将数组排序,用一个 pair记录数字在原数中的位置就不影响答案。
  • 对于第 i个数s
    – 先计算比s小的数x的个数有(i - 1)个,我们假设这些比s小的数的都有 1 ∼ s 1 \sim s 1s的贡献值,那么总贡献为(i - 1) * s,因为有些数不能贡献那么多,所以需要将多加的贡献值减去,需要减去的贡献值就是pre[i - 1],减完发现多减去了i - 11的贡献值,所以最后还需要加上。
    – 再计算比s大的数x的个数有(n - i)个,我们假设这些比s大的数的都有 1 ∼ x 1 \sim x 1x的贡献值,那么总贡献为suf[i + 1],因为有些数不能贡献那么多,所以需要将多加的贡献值减去,需要减去的贡献值就是s * (n - i),减完发现多减去了n - i1的贡献值,所以最后还需要加上。
    – 最后加上s本身贡献的一个。
public static void solve() throws IOException {
    int n = readInt();
    Pair[] pairs = new Pair[n + 1];
    Arrays.setAll(pairs, g -> new Pair());
    long[] pre = new long[n + 5], suf = new long[n + 5];

    for (int i = 1; i <= n; i++) {
        pairs[i].first = readInt(); pairs[i].second = i;
    }
    Arrays.sort(pairs, (o1, o2) -> o1.first - o2.first);
    for (int i = 1, j = n; i <= n; i++, j--) {
        pre[i] = pre[i - 1] + pairs[i].first;
        suf[j] = suf[j + 1] + pairs[j].first;
    }
    long[] res = new long[n + 1];
    for (int i = 1; i <= n; i++) {
        res[pairs[i].second] = ((long) pairs[i].first * (i - 1) - pre[i - 1] + (i - 1)) +
                (suf[i + 1] - (long) pairs[i].first * (n - i) + (n - i)) + 1;
    }

    for (int i = 1; i <= n; i++) printWriter.print(res[i] + " ");
    printWriter.println();
}

你可能感兴趣的:(数学,分类讨论,前缀和,算法,java)