CodeForces Gym 101981 简要题解

本文详细介绍了CodeForces Gym 101981比赛中涉及的算法和策略,包括Adrien和Austin的特判策略、Tournament的决策单调性DP、Cherry和Chocolate的重心选择法、Country Meow的三分法、Eva和Euro coins的最小表示法、Frank的期望步数计算等。通过具体问题解析,深入探讨了解题思路。
摘要由CSDN通过智能技术生成

Adrien and Austin

特判 n = 0 n=0 n=0 或者 k = 1 k=1 k=1 的情况,当 k > 1 k>1 k>1 的时候,先手取中间,然后后手不管取什么先手取相对的,所以先手必胜。

#include <bits/stdc++.h>

using namespace std;

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  if (m == 1) {
   
    puts(n & 1 ? "Adrien" : "Austin");
  } else {
   
    puts(n ? "Adrien" : "Austin");
  }
  return 0;
}

Tournament

凸优化+决策单调性DP。

#include <bits/stdc++.h>

using namespace std;

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  vector<long long> sum(n + 1);
  vector<long long> f(n + 1);
  vector<int> g(n + 1);
  for (int i = 1; i <= n; ++i) {
   
    scanf("%lld", &sum[i]);
    sum[i] += sum[i - 1];
  }
  vector<pair<int, int>> q(n);
  auto solve = [&](long long cost) {
   
    int ll = 0, rr = 0;
    auto get = [&](int l, int r) {
   
      return f[l] + cost + sum[l] + sum[r] - sum[l + r >> 1] - sum[l + r + 1 >> 1];
    };
    for (int i = 0; i < n; ++i) {
   
      while (ll < rr && get(i, q[rr - 1].second) < get(q[rr - 1].first, q[rr - 1].second)) {
   
        --rr;
      }
      if (ll == rr) {
   
        q[rr++] = make_pair(i, i + 1);
      } else {
   
        int l = q[rr - 1].second, r = n + 1;
        while (l < r) {
   
          int mid = l + r >> 1;
          if (get(i, mid) < get(q[rr - 1].first, mid)) {
   
            r = mid;
          } else {
   
            l = mid + 1;
          }
        }
        if (r <= n) {
   
          q[rr++] = make_pair(i, r);
        }
      }
      while (ll + 1 < rr && q[ll + 1].second <= i + 1) {
   
        ++ll;
      }
      f[i + 1] = get(q[ll].first, i + 1);
      g[i + 1] = g[q[ll].first] + 1;
    }
  };
  long long l = 0, r = sum[n];
  while (l < r) {
   
    long long mid = l + r >> 1;
    solve(mid);
    if (g[n] <= m) {
   
      r = mid;
    } else {
   
      l = mid + 1;
    }
  }
  solve(r);
  printf("%lld\n", f[n] - r * m);
  return 0;
}

Cherry and Chocolate

考虑第一个人选了一个点之后,第二个人一定会选择某个子树的重心,然后第一个人删去最大的那个子树。这时候我们求出了一个最优解,假设第一个人选的点不在第二个人当前这个解所在子树内,那么第二个人保持同样的决策答案不会对第一个人更优。每次选重心之后朝一个子树递归即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<vector<int>> adj(n);
  for (int i = 0; i < n - 1; ++i) {
   
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    adj[x].push_back(y);
    adj[y].push_back(x);
  }
  int answer = 0;
  vector<int> size(n), parent(n);
  vector<bool> visit(n);
  auto find_centroid = [&](int x, bool ban) {
   
    vector<int> order(1, x);
    for (int i = 0; i < order.size(); ++i) {
   
      int x = order[i];
      for (auto y : adj[x]) {
   
        if ((!visit[y] || !ban) && y != parent[x]) {
   
          order.push_back(y);
          parent[y] = x;
        }
      }
    }
    int root = -1, value = n;
    for (int i = order.size() - 1; ~i; --i) {
   
      int x = order[i], subtree = 0;
      size[x] = 1;
      for (auto y : adj[x]) {
   
        if ((!visit[y] || !ban) && y != parent[x]) {
   
          size[x] += size[y];
          subtree = max(subtree, size[y]);
        }
      }
      subtree = max(subtree, (int)order.size() - size[x]);
      if (subtree < value) {
   
        root = x;
        value = subtree;
      }
    }
    return make_pair(root, value + n - (int)order.size());
  };
  auto get = [&](int x) {
   
    pair<int, int> result = make_pair(n, -1);
    for (auto y : adj[x]) {
   
      parent[y] = x;
      result = min(result, make_pair(find_centroid(y, false).second, y));
    }
    return result;
  };
  int x = 0;
  while (!visit[x]) {
   
    parent[x] = -1;
    x = find_centroid(x, true).first;
    visit[x] = true;
    pair<int, int> result = get(x);
    answer = max(answer, result.first);
    x = result.second;
  }
  printf("%d\n", answer);
  return 0;
}

Country Meow

三分套三分套三分。

#include <bits/stdc++.h>

using namespace std;

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<vector<int>> a(3, vector<int> (n));
  for (int i = 0; i < n; ++i) {
   
    for (int j = 0; j < 3; ++j) {
   
      scanf("%d", &a[j][i]);
    }
  }
  vector<double> p(3);
  function<double(int)> ternary_search = [&](int x) {
   
    if (x == 3) {
   
      double answer = 0;
      for (int i = 0; i < n; ++i) {
   
        double result = 0;
        for (int j = 0; j < 3; ++j) {
   
          result += (p[j] - a[j][i]) * (p[j] - a[j][i]);
        }
        answer = max(answer, result);
      }
      return answer;
    } else {
   
      double l = *min_element(a[x].begin(), a[x].end());
      double r = *max_element(a[x].begin(), a[x].end()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值