Atcoder Grand Contest 030 简要题解

本文精选了五道算法竞赛题目并提供了详细的解题思路和代码实现,包括PoisonousCookies、TreeBurning、ColoringTorus、InversionSum和PermutationandMinimum,涵盖了概率计算、最优化问题、构造算法、动态规划等多个方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Poisonous Cookies

答案是 b+min⁡(c,a+b+1)b+\min(c,a+b+1)b+min(c,a+b+1)

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int a, b, c;
  scanf("%d %d %d", &a, &b, &c);
  printf("%d\n", b + min(c, a + b + 1));
  return 0;
}

Tree Burning

考虑最后一定存在一个分界点,在两个分界点之间反复横跳。枚举方向和分界点,前缀和计算答案即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int l, n;
  scanf("%d %d", &l, &n);
  vector<int> x(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &x[i]);
  }
  long long answer = 0;
  for (int rotate = 0; rotate < 2; ++rotate) {
    vector<int> p = x;
    p.push_back(l);
    for (int i = 0; i < n; ++i) {
      p.push_back(l + x[i]);
    }
    vector<long long> sum(n * 2 + 1);
    for (int i = 0; i < n * 2; ++i) {
      sum[i + 1] = sum[i] + 2ll * i * (p[i + 1] - p[i]);
    }
    for (int i = 0; i <= n; ++i) {
      int left = n - i, right = i;
      int left_l = i, left_r = n;
      int right_l = n, right_r = n + i;
      long long result = 0;
      if (left > right) {
        int diff = left - right;
        result += (long long) (p[left_r] - p[left_r - diff]) * (2 * right + 1);
        left_r -= diff;
      }
      if (left < right) {
        int diff = right - left;
        result += (long long) (p[right_l + diff] - p[right_l]) * (2 * left);
        right_l += diff;
      }
      result += (sum[left_r] - sum[left_l]) - (long long) (p[left_r] - p[left_l]) * (2 * left_l - 1);
      result += (long long) (p[right_r] - p[right_l]) * (2 * right_r) - (sum[right_r] - sum[right_l]);
      answer = max(answer, result);
    }
    for (int i = 0; i < n; ++i) {
      x[i] = l - x[i];
    }
    reverse(x.begin(), x.end());
  }
  printf("%lld\n", answer);
  return 0;
}

Coloring Torus

考虑 nnn 是偶数时,构造:ci,j=((i+j)&VeryThinSpace;mod&VeryThinSpace;n)+(i&VeryThinSpace;mod&VeryThinSpace;2)×nc_{i,j} = ((i+j)\bmod n) + (i\bmod 2)\times nci,j=((i+j)modn)+(imod2)×n ,显然符合要求。

注意到将所有 i+ni+ni+n 换成 iii 也是合法的,所以可以构造出 [n,2n][n,2n][n,2n] 中的所有 kkk

特判 k=1k=1k=1 的情况,令 n=2⌈k4⌉n=2\lceil \frac{k}{4}\rceiln=24k 即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int k;
  scanf("%d", &k);
  if (k == 1) {
    puts("1");
    puts("1");
    return 0;
  }
  int n = (k + 3) / 4 * 2;
  printf("%d\n", n);
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      int color = (i + j) % n;
      if ((i & 1) && color + n < k) {
        color += n;
      }
      printf("%d%c", color + 1, j == n - 1 ? '\n' : ' ');
    }
  }
  return 0;
}

Inversion Sum

fi,jf_{i,j}fi,j 表示 ai&gt;aja_i&gt;a_jai>aj 的概率,转移时枚举与交换的位置有关的状态,只有 O(n)O(n)O(n) 个。

#include <bits/stdc++.h>

using namespace std;

const int md = 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline int mul(int x, int y) {
  return (long long) x * y % md;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  vector<vector<int>> f(n, vector<int> (n));
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      f[i][j] = a[i] > a[j];
    }
  }
  int coef = 1;
  while (m--) {
    coef = mul(coef, 2);
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    f[x][y] = f[y][x] = mul(f[x][y] + f[y][x], md + 1 >> 1);
    for (int i = 0; i < n; ++i) {
      if (i != x && i != y) {
        f[i][x] = f[i][y] = mul(f[i][x] + f[i][y], md + 1 >> 1);
        f[x][i] = f[y][i] = mul(f[x][i] + f[y][i], md + 1 >> 1);
      }
    }
  }
  int answer = 0;
  for (int i = 0; i < n; ++i) {
    for (int j = i + 1; j < n; ++j) {
      add(answer, f[i][j]);
    }
  }
  printf("%d\n", mul(answer, coef));
  return 0;
}

Less than 3

考虑在 010101 之间画一条红线, 101010 之间画一条蓝线,并在边界补充上无限的红蓝交替的线,那么一次操作就是将一条线移动一个位置(可以参考官方题解的图)。如果我们知道的线的对应关系,那么答案下界是至少移动的距离,同时可以证明这样的方案一定存在。一共只有 O(n)O(n)O(n) 种对应关系,枚举之后计算答案即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  string s, t;
  cin >> n >> s >> t;
  if (n <= 2) {
    int answer = 0;
    for (int i = 0; i < n; ++i) {
      if (s[i] != t[i]) {
        ++answer;
      }
    }
    printf("%d\n", answer);
    return 0;
  }
  vector<pair<int, int>> diff_s, diff_t;
  for (int i = 0; i < n - 1; ++i) {
    if (s[i] != s[i + 1]) {
      diff_s.emplace_back(i, s[i] - '0');
    }
    if (t[i] != t[i + 1]) {
      diff_t.emplace_back(i, t[i] - '0');
    }
  }
  int answer = INT_MAX;
  auto solve = [&](vector<pair<int, int>> a, vector<pair<int, int>> b) {
    if (a.size() > b.size()) {
      swap(a, b);
    }
    reverse(a.begin(), a.end());
    while (a.size() < b.size()) {
      a.emplace_back(-1, !a.back().second);
    }
    reverse(a.begin(), a.end());
    if (a.back().second == b.back().second) {
      int result = 0;
      for (int i = 0; i < a.size(); ++i) {
        result += abs(a[i].first - b[i].first);
      }
      answer = min(answer, result);
    }
  };
  for (int rotate = 0; rotate < 2; ++rotate) {
    vector<pair<int, int>> new_diff_t = diff_t;
    for (int i = 0; i <= n; ++i) {
      solve(diff_s, new_diff_t);
      new_diff_t.emplace_back(n - 1, !new_diff_t.back().second);
    }
    swap(diff_s, diff_t);
  }
  printf("%d\n", answer);
  return 0;
}

Permutation and Minimum

考虑一对 (A2i−1,A2i)(A_{2i-1}, A_{2i})(A2i1,A2i) ,如果它们都有值了就直接扔掉,剩下的只有两种:(−1,−1)(-1, -1)(1,1)(−1,x)(-1, x)(1,x) 。对于 (−1,−1)(-1, -1)(1,1) ,我们忽略它们的位置关系,最后答案乘上其个数的阶乘。

考虑从大到小填数,记 f(i,j,k)f(i,j,k)f(i,j,k) 表示当前考虑到 iii ,有 jjj−1-11 没有匹配,有 kkkxxx 没有匹配,考虑转移:

  • 如果 iii 是某个 xxx

    • 不匹配,转移到 f(i−1,j,k+1)f(i-1,j,k+1)f(i1,j,k+1)

    • 匹配一个 −1-11 ,转移到 f(i−1,j−1,k)f(i-1,j-1,k)f(i1,j1,k)

  • 如果 iii 不是某个 xxx

    • 不匹配,转移到 f(i−1,j+1,k)f(i-1,j+1,k)f(i1,j+1,k)
    • 匹配一个 −1-11 ,因为是无序的所以直接转移到 f(i−1,j−1,k)f(i-1,j-1,k)f(i1,j1,k)
    • 匹配一个 xxx ,因为 xxx 的位置有关所以有 kkk 种方案转移到 f(i−1,j,k−1)f(i-1,j,k-1)f(i1,j,k1)
#include <bits/stdc++.h>

using namespace std;

const int md = 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline int mul(int x, int y) {
  return (long long) x * y % md;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<bool> used(n * 2);
  vector<bool> single(n * 2);
  int free = 0;
  for (int i = 0; i < n; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    if (~x) {
      --x;
    }
    if (~y) {
      --y;
    }
    if (~max(x, y)) {
      if (~min(x, y)) {
        used[x] = used[y] = true;
      } else {
        single[max(x, y)] = true;
      }
    } else {
      ++free;
    }
  }
  vector<vector<int>> dp(n + 1, vector<int> (n * 2 + 1));
  dp[0][0] = 1;
  for (int i = n * 2 - 1; ~i; --i) {
    if (!used[i]) {
      vector<vector<int>> new_dp(n + 1, vector<int> (n * 2 + 1));
      for (int j = 0; j <= n; ++j) {
        for (int k = 0; k <= n * 2; ++k) {
          if (dp[j][k]) {
            if (single[i]) {
              add(new_dp[j + 1][k], dp[j][k]);
              if (k) {
                add(new_dp[j][k - 1], dp[j][k]);
              }
            } else {
              add(new_dp[j][k + 1], dp[j][k]);
              if (j) {
                add(new_dp[j - 1][k], mul(j, dp[j][k]));
              }
              if (k) {
                add(new_dp[j][k - 1], dp[j][k]);
              }
            }
          }
        }
      }
      swap(dp, new_dp);
    }
  }
  int answer = dp[0][0];
  for (int i = 1; i <= free; ++i) {
    answer = mul(answer, i);
  }
  printf("%d\n", answer);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值