Educational Codeforces Round 126 A~D

A. Array Balancing

题意:
给两个数组,给两个操作选择。
1:选择一个整数i(我也不知道这个操作有啥实际意义)
2:交换 Ai 和Bi 的值。
使 ∑ 1 n \sum_1^n 1n ∣ ( A i − A i − 1 ) ∣ |(A_i-A_{i-1})| (AiAi1) + ∣ ( B i − B i − 1 ) ∣ |(B_i-B_{i-1})| (BiBi1)最小。
解题思路:数学关系 如果发现 A i > B i A_i>B_i Ai>Bi就换。(把值大的换到一个数组,值小的放一个数组)
换完直接加就行了。

 for (int i = 1; i <= n; i++)
      if (a[i] > b[i])
        swap(a[i], b[i]);
    rep(i, 2, n)
        sum += abs(a[i] - a[i - 1]) + abs(b[i] - b[i - 1]);
    cout << sum << endl;

收获:写这篇博客的时候对于markdown公式的写法收获大于该题。

B. Getting Zero

题意:
给你一个数(注意是数不是数组)
可以有以下两种操作:

  • v = ( v + 1 ) m o d 32768 v = (v+1) mod 32768 v=(v+1)mod32768
  • v = ( 2 ⋅ v ) m o d 32768 v=(2⋅v)mod32768 v=(2v)mod32768

把这个数字变成零的最小操作次数。

解题思路:
首先32768不是随便的数字是 2 15 2^{15} 215,任何数 X X X乘以 2 15 2^{15} 215再取余 2 15 2^{15} 215都等于 0 0 0.因此首先本题答案最大为: 2 15 2^{15} 215即32768。
后经过验算得知先加后乘是最优的直接暴力跑就完事了。

int solve(int k)
{
    
    
  int ans = INT_MAX;
  for (int i = 0; i <= 15; i++)
  {
    
    
    for (int j = 0; j <= 15; j++)
    {
    
    
      if (((k + i) * (1 << j)) % 32768 == 0)
      {
    
    
        if (i + j < ans)
          ans = i + j;
      }
    }
  }
  return ans;
}

C. Water the Trees

题意:
n n n棵树,有两种操作。

  • 奇数天浇水长高1,偶数天2.
  • 不浇水。

需要几天树一样高。

解题思路:
贪心思想,所需的高度是max或max+1,其中max是某个树的最大初始高度。我们不需要大于max+1的高度,因为,比如说,如果高度是max+2,我们可以删除一些动作,得到高度为max的答案。同样的事情也适用于所有大于max+1的高度。为什么我们甚至需要max+1的高度呢?在某些情况下(如[1,1,1,1,1,1,2]),高度max+1的答案比高度max的答案更好(在这个特定的例子中,是9对11)。所以我们假设用二进制搜索。让当前的答案是mid。然后让cnt1=⌈mid2⌉是我们可以做的+1操作的数量,cnt2=⌊mid2⌋是我们可以做的+2操作的数量。我们可以贪婪地使用+2操作,然后只需检查+1操作的数量是否足以增长其余的高度。

	cin >> n;
    for (int i = 0; i < n; i++)
      cin >> a[i];
    int mx = *max_element(a, a + n);
    int ans = LLONG_MAX;
    for (int x : {
    
    mx, mx + 1})
    {
    
    
      int cnt1 = 0, cnt2 = 0;
      for (int i = 0; i < n; i++)
      {
    
    
        cnt1 += (x - a[i]) % 2;
        cnt2 += (x - a[i]) / 2;
      }
      long long dif = max(0ll, cnt2 - cnt1 - 1) / 3;
      cnt1 += dif * 2;
      cnt2 -= dif;
      ans = min(ans, max(cnt1 * 2 - 1, cnt2 * 2));
      if (cnt2 > 0)
      {
    
    
        cnt1 += 2;
        --cnt2;
        ans = min(ans, max(cnt1 * 2 - 1, cnt2 * 2));
      }
    }
  
    cout << ans << endl;

D. Progressions Covering

题意:
给你两个数组:一个由n个零组成的数组a和一个由n个整数组成的数组b。

你可以对数组a进行任意次数的操作:选择一个长度为k的子段,并将算术级数1,2,…,k加到这个子段上–即在子段的第一个元素上加1,在第二个元素上加2,以此类推。所选的子段应该在数组a的边界内(即如果所选子段的左边界是l,那么应该满足1≤l≤l+k-1≤n的条件)。注意添加的级数总是1,2,…,k,而不是k,k-1,…,1或其他什么(即子段的最左边的元素总是增加1,第二个元素总是增加2,以此类推)。

问最少多少次,可以使得整个序列中的数,都小于等于0。

解题思路:
倒着找,遍历一下子都减去k看是怎么样的。u示我们需要从当前元素中减去当前存在的进位的值。a是当前存在的进位的数量。d[i]表示将在i+1位置结束的进位的数量(即不会从i位置再向左增加任何东西)。

#include <bits/stdc++.h>

// 代码由偶像jangly赞助

int main()
{
    
    
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);

  int n, k;
  std::cin >> n >> k;

  std::vector<long long> b(n);
  for (int i = 0; i < n; i++)
  {
    
    
    std::cin >> b[i];
  }

  long long ans = 0;
  long long u = 0, a = 0;
  std::vector<long long> c(n), d(n);
  for (int i = n - 1; i >= 0; i--)
  {
    
    
    u += c[i];
    a += d[i];
    b[i] += u * i + a;
    long long v = std::min(i + 1, k);
    if (b[i] > 0)
    {
    
    
      long long t = (b[i] + v - 1) / v;
      u -= t;
      a -= t * (v - i);
      ans += t;
      if (i - v >= 0)
      {
    
    
        c[i - v] += t;
        d[i - v] += t * (v - i);
      }
    }
  }

  std::cout << ans << "\n";

  return 0;
}

总结:本场edu主要考查了多种操作方式下的贪心思路题,D题还有点不太熟悉,希望后来可以更加完善,但明显能看出来偶像jiangly的思路和官方题解大致上一模一样。很佩服人家都说怎么一眼出来的,以后还是要多加练习。

猜你喜欢

转载自blog.csdn.net/qq_45148277/article/details/126257838