Practice

A. Absolute Maximization

题意

给定一个数组 \(a\),定义操作为选择两个元素 \(a_i, a_j\),并交换它们二进制下的第 \(b\) 位。输出任意次操作后的 \(\max(a) - \min(a)\) 的最大值。

思路

既然可以无限次交换,那么我们只要找出最高位,从最高位开始往下找,只要有一个元素该位存在 \(1\),那么我们就拿过来构建新的数字,这样即可得到最大值。反之同理。

时间复杂度:\(O(n)\)

对应AC代码

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        for(int w=0;w<t;w++){
            int n = scanner.nextInt();
            int[] min = new int[12], max = new int[12];
            for(int i=0;i<12;i++){
                min[i] = 1;
                max[i] = 0;
            }
            int maxLen = 0;
            String[] input = new String[n];
            for(int i=0;i<n;i++) {
                input[i] = Integer.toBinaryString(scanner.nextInt());
                maxLen = Math.max(maxLen, input[i].length());
            }
            for(int i=0;i<n;i++) {
                while(input[i].length() != maxLen) input[i] = "0" + input[i];
                for(int j=0;j<maxLen;j++) {
                    int now = input[i].charAt(j) - '0';
                    max[j] = Math.max(max[j], now);
                    min[j] = Math.min(min[j], now);
                }
            }
            int maxx = 0, minn = 0;
            for(int i=0;i<maxLen;i++) {
                maxx = maxx * 2 + max[i];
                minn = minn * 2 + min[i];
            }
            System.out.println(maxx - minn);
        }
    }
}

简单思维题

B. Incinerate

题意

给定 \(n\) 个怪物的生命值 \(h_i\) 以及攻击力 \(p_i\),主角的攻击力为 \(k\),定义一次攻击为将所有怪物扣去 \(k\) 点生命值,生命值小于等于 \(0\) 的怪物死亡,剩余攻击力最低的怪物将会将主角的攻击力削减到 \(k - p_i\)。输出是否可以将怪打完。

思路

模拟。

我们可以先按照生命值升序排序,维护一个存活的怪物的开始下标 \(index\),那么想要快速获取到存活的怪物中攻击力最低的,我们可以维护一个后缀数组,存储 \(index\) 及以后的 \(p_{\min}\)

时间复杂度:\(O(n \log n)\)

对应AC代码

import java.util.*;

public class Main {

    private static class Monster{
        int h, p;
        Monster(int h){
            this.h = h;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        for(int w=0;w<t;w++){
            int n = scanner.nextInt(), k = scanner.nextInt();
           Monster[] m = new Monster[n];
           for(int i=0;i<n;i++) m[i] = new Monster(scanner.nextInt());
           for(int i=0;i<n;i++) m[i].p = scanner.nextInt();
           Arrays.sort(m, Comparator.comparingInt(o -> o.h));
           int[] suf = new int[n + 1];
           suf[n] = Integer.MAX_VALUE;
           for(int i=n-1;i>=0;i--) suf[i] = Math.min(suf[i + 1], m[i].p);
           int index = 0, attack = 0;
           while(k > 0){
               for(int i=index;i<n;i++){
                   if(m[i].h - attack > k) break;
                   index ++;
               }
               if(index >= n) break;
               attack += k;
               k -= suf[index];
           }
           System.out.println(k > 0 ? "YES" : "NO");
        }
    }
}

略微暴力但又不暴力(

C. Another Array Problem

题意

给定一个数组 \(a\),定义操作为选择 \(i, j,i \neq j\),将所有 \(a_k, k \in [i, j]\) 修改为 \(|a_i - a_j|\)。在任意次操作后,输出数组的总和的最大值。

思路

我们来考虑一下 \(4\) 个及以上的情况:

在这个情况里,我们不妨找出最大值所在的下标 \(imax\),然后用类似下面的思路完成:

\(\begin{array}{l}>>1\ 2\ 4\ 3 \\ => 1\ 1\ 4\ 3 \\ => 0\ 0\ 4\ 3 \\ => 4\ 4\ 4\ 3 \\ => 4\ 4\ 1\ 1 \\ => 4\ 4\ 0\ 0 \\ => 4\ 4\ 4\ 4\end{array}\)

也就是说,我们只需找出最大的值,最后一定有方案将所有数全都改为最大值。

那么 \(3\) 个数呢?此时存在局限性,若最大值在两端,那么和上述一致,但最大值在中间时,我们就只能找出两端的 \(\min , \max\),然后取 \(a[1]-\min\)\(\max\) 的最大值。

同上,两个数的时候,最大值即为两个元素的差。

当然,我们也可以不操作,所以需要取一下操作后的答案和原总和的最大值。

时间复杂度:\(O(n)?\)

对应AC代码

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        for(int w=0;w<t;w++){
            int n = scanner.nextInt();
            long[] a = new long[n];
            int maxIndex = 0;
            long sum = 0;
            for(int i=0;i<n;i++){
                a[i] = scanner.nextLong();
                sum += a[i];
                maxIndex = a[maxIndex] < a[i] ? i : maxIndex;
            }
            long ans;
            if(n == 2) {
                ans = Math.abs(a[1] - a[0]) * 2;
            }else if(n == 3){
                if(maxIndex == 0 || maxIndex == 2) ans = a[maxIndex] * n;
                else{
                    long min = Math.min(a[0], a[2]), sMin = Math.max(a[0], a[2]);
                    ans = Math.max(a[maxIndex] - min, sMin) * 3;
                }
            }else ans = a[maxIndex] * n;
            System.out.println(Math.max(sum, ans));
        }
    }
}

很巧妙的思维题