Codeforces Round 907 (Div. 2) 题解 A-B

A - Sorting with Twos

原题链接

题目描述
给你一个长度为 n n n 的数组 a a a,在每次操作中,你可以选择一个非负整数 m m m ( 2 m ≤ n 2^m \leq n 2mn),然后让所有的 a i a_i ai ( 1 ≤ i ≤ 2 m 1 \le i \leq 2^m 1i2m)都减去 1 1 1,问是否可以通过一定次数使得数组 a a a 变成非递减数组。

思路:思维+暴力修改

  • 由于 n n n很小,我们每次从坐标 1 1 1开始,往后暴力修改每一个长度为 2 m 2^m 2m的区间,这样一定可以让前面的更小,如果这样修改都无法满足,那么为No
public static void solve() throws IOException{
    int n = readInt();
    int[] arr = utils.nextIntArray(n);
    int m = 0;
    boolean f = true;
    int k = 10;
    while (k-- > 0) {
    	// 如果在修改区间时,已经遇到arr[i - 1] > arr[i],那么肯定无法满足题意
    	// 因为该区间已经不可再分,即无法将arr[i - 1]变为更小的值
        for (int i = 1; i <= Math.pow(2, m) && i <= n; i++) {
            if (arr[i - 1] > arr[i]) {
                f = false;
                break;
            }
        }
        if (!f) break;
        // 暴力将前面都修改为最小值
        for (int i = 1; i <= Math.pow(2, m) && i <= n; i++) {
            arr[i] = 0;
        }
        m++;// 增加区间长度
    }
    printWriter.println(f ? "YES" : "NO");
}

B - Deja Vu

原题链接

题目描述
给你一个长度为 n n n 的正整数数组 a a a 和一个长度为 q q q 的正整数数组 x x x,一共有 q q q 次修改,对于每次修改,如果 a j ( 1 ≤ j ≤ n ) a_j(1 \leq j \leq n) aj(1jn)可以被 2 x i ( 1 ≤ i ≤ q ) 2^{x_i}(1 \leq i \leq q) 2xi(1iq)整除,那么就把 2 x i − 1 2^{x_i-1} 2xi1 加到 a j a_j aj 上。求在完成 q q q 次修改后的 a a a 数组。

思路:思维+观察

  • 如果 a j a_j aj 可以被 2 x i 2^{x_i} 2xi整除,那么当 a j a_j aj加上 2 x i − 1 2^{x_{i-1}} 2xi1后, a j a_j aj之后可以整除的 2 x k 2^{x_k} 2xk 一定小于 2 x i 2^{x_i} 2xi,即 k < i k \lt i k<i,比如 32 32 32 可以整除 2 5 2^5 25 ( i = 5 ) (i = 5) (i=5),那么 32 32 32 加上 2 4 ( i − 1 = 4 ) 2^4(i-1 = 4) 24(i1=4)后变为 48 48 48 ,而 48 48 48 只能整除 2 p ( p < 5 ) 2^p(p \lt 5) 2p(p<5)
  • 所以对于每一个 a j a_j aj,当他可以整除 x i x_i xi时,求出 x i x_i xi x x x数组中的严格下降子序列即可。但是 x i x_i xi的严格下降子序列我们应该预处理好。
public static void solve() throws IOException{
    int n = readInt(), q = readInt();
    int[] a = utils.nextIntArray(n);
    int[] x = utils.nextIntArray(q);
    List<Integer>[] lists = new List[40];
    Arrays.setAll(lists, g -> new ArrayList<>());
    for (int i = 1; i <= 30; i++) {
        lists[i].add(40);// xi最大为30,这里我们添加一个更大的值,保证降序
    }
    // 求出 xi的严格下降序列
    for (int i = 1; i <= 30; i++) {
        for (int j = 1; j <= q; j++) {
            if (x[j] < lists[i].get(lists[i].size() - 1)) {//比上一次的小就添加
                lists[i].add(x[j]);
            }
        }
    }
    int[] have= new int[40];// xi是否出现过
    for (int i = 1; i <= q; i++) {
        if (have[x[i]] == 0) {
            have[x[i]] = i;
        }
    }
    for (int i = 1; i <= n; i++) {
    	// 求出 ai的因数可以有多少个 2
        int k = 0, t = a[i];
        while (t % 2 == 0 && t != 0) {
            k++;
            t /= 2;
        }
        // 求出最近的能被 ai整除的 x数组的数 x^k
        while (k >= 1 && have[k] == 0) {
            k--;
        }
        // 根据 x^k的严格下降序列,求出最终的 ai
        List<Integer> ps = lists[k];
        for (Integer p : ps) {
            if (a[i] % Math.pow(2, p) == 0) {
                a[i] += Math.pow(2, (p - 1));
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        printWriter.print(a[i] + " ");
    }
    printWriter.println();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值