CodeForces Gym 101623 简要题解

Ascending Photo

首先离散化,并将相邻的相同的数变成一个。考虑对值域从小到大DP,那么如果 i i i+1 i + 1 之间没有被切开那么 i+1 i + 1 i+2 i + 2 之间可能就必须切开,所以只需要维护DP值和上次决策点即可。不难发现只维护最大值和次大值就可以转移了。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  vector<int> disc = a;
  sort(disc.begin(), disc.end());
  disc.erase(unique(disc.begin(), disc.end()), disc.end());
  vector<vector<int>> all(disc.size() + 1);
  vector<int> b;
  for (int i = 0; i < n; ++i) {
    a[i] = lower_bound(disc.begin(), disc.end(), a[i]) - disc.begin();
    if (!b.empty() && a[i] == b.back()) {
      continue;
    }
    all[a[i]].push_back(b.size());
    b.push_back(a[i]);
  }
  n = b.size();
  pair<int, int> u = make_pair(0, n), v = make_pair(0, n);
  for (int i = 0; i < disc.size(); ++i) {
    pair<int, int> x = make_pair(u.first, n), y = make_pair(v.first, n);
    for (auto p : all[i]) {
      if (p != n - 1 && b[p] + 1 == b[p + 1]) {
        pair<int, int> value(1 + (u.second + 1 != p ? u.first : v.first), all[i + 1].size() == 1 ? n : p);
        y = max(y, value);
        if (x < y) {
          swap(x, y);
        }
      }
    }
    u = x;
    v = y;
  }
  printf("%d\n", n - 1 - u.first);
  return 0;
}

Boss Battle

特判 n3 n ≤ 3 的情况,否则每次可以把boss可能的位置减少 1 1 ,答案是 n2 n − 2

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  printf("%d\n", max(n - 2, 1));
  return 0;
}

Connect the Dots

贪心,维护当前合法的角度区间即可。

#include 

using namespace std;

struct angle_t {
  int x, y, turn;

  angle_t(int x = 0, int y = 0, int turn = 0):x(x), y(y), turn(turn) {
  }

  angle_t operator - (const angle_t &b) const {
    return angle_t(x - b.x, y - b.y, turn);
  }

  int where() const {
    if (y < 0) {
      if (x >= 0) {
        return 3;
      } else {
        return 2;
      }
    } else if (y > 0) {
      if (x <= 0) {
        return 1;
      } else {
        return 0;
      }
    } else {
      if (x <= 0) {
        return 2;
      } else {
        return 0;
      }
    }
  }

  angle_t rotate() {
    return angle_t(-x, -y, turn + (where() >= 2));
  }

  bool operator < (const angle_t &b) const {
    if (turn != b.turn) {
      return turn < b.turn;
    } else if (where() != b.where()) {
      return where() < b.where();
    } else {
      return x * b.y > y * b.x;
    }
  }

  bool operator <= (const angle_t &b) const {
    return !(b < *this);
  }

  bool operator > (const angle_t &b) const {
    return b < *this;
  }

  bool operator >= (const angle_t &b) const {
    return !(*this < b);
  }
};

struct range_t {
  angle_t l, r;
  bool tl, tr;

  range_t() {
  }

  range_t(angle_t angle) {
    l = r = angle;
    tl = tr = true;
  }

  bool in(angle_t angle) {
    while (!(tl ? l <= angle : l < angle)) {
      ++angle.turn;
    }
    return tr ? angle <= r : angle < r;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n = 4, answer = 1;
  vector a(n * n);
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      int x;
      scanf("%d", &x);
      a[x - 1] = angle_t(i, j, 0);
    }
  }
  range_t current(a[1] - a[0]);
  for (int i = 2; i < n * n; ++i) {
    angle_t delta = a[i] - a[i - 1];
    if (current.in(delta)) {
      current = range_t(delta);
    } else {
      ++answer;
      angle_t l = current.l.rotate(), r = current.r.rotate();
      while (r > delta.rotate()) {
        --r.turn;
      }
      while (r.rotate() <= delta) {
        ++r.turn;
      }
      while (l >= delta.rotate()) {
        --l.turn;
      }
      while (l.rotate() < delta) {
        ++l.turn;
      }
      current = range_t(delta);
      if (r > delta) {
        current.r = r;
        current.tr = false;
      }
      if (l < delta) {
        current.l = l;
        current.tl = false;
      }
      int diff = -current.l.turn;
      current.l.turn += diff;
      current.r.turn += diff;
    }
  }
  printf("%d\n", answer);
  return 0;
}

Dunglish

模拟。

#include 

using namespace std;

typedef long long ll;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  int n;
  cin >> n;
  vector<string> a(n);
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
  }
  int m;
  cin >> m;
  map<string, int> correct, incorrect;
  map<string, string> translation;
  while (m--) {
    string from, to, state;
    cin >> from >> to >> state;
    if (state == "correct") {
      ++correct[from];
    } else {
      ++incorrect[from];
    }
    translation[from] = to;
  }
  ll ways = 1, ok = 1;
  for (int i = 0; i < n; ++i) {
    ways *= (correct[a[i]] + incorrect[a[i]]);
    ok *= correct[a[i]];
  }
  if (ways == 1) {
    for (int i = 0; i < n; ++i) {
      cout << translation[a[i]] << (i == n - 1 ? '\n' : ' ');
    }
    cout << (ok ? "correct" : "incorrect") << endl;
  } else {
    cout << ok << " correct" << endl;
    cout << ways - ok << " incorrect" << endl;
  }
  return 0;
}

English Restaurant

将桌子按照容量排序,并在最后新增 t t 个容量为 的桌子。最后人一定是坐满了若干个区间,首先预处理 f[l][r] f [ l ] [ r ] 表示只坐了区间 [l,r] [ l , r ] 的桌子的方案数和答案总和,转移枚举最后一个坐的桌子 mid m i d ,两边是子问题,而最后一次来的人数一定在区间 (al1,amid] ( a l − 1 , a m i d ] 内,就可以转移了。

从后往前DP,记 g[i][j] g [ i ] [ j ] 表示考虑 i i 之后的时刻,当前区间最左端点是 j j 的方案数和答案总和,转移的时候枚举上一次转移的时刻和上一次区间的开始位置,用前缀和优化即可。

#include 

using namespace std;

typedef long double ld;

pair operator + (pair a, pair b) {
  return make_pair(a.first + b.first, a.second + b.second);
}

pair operator * (pair a, pair b) {
  return make_pair(a.first * b.second + a.second * b.first, a.second * b.second);
}

int sum(int x) {
  return x * (x + 1) >> 1;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, group, hours;
  scanf("%d %d %d", &n, &group, &hours);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
    a[i] = min(a[i], group);
  }
  sort(a.begin(), a.end());
  for (int i = 0; i < hours; ++i) {
    a.push_back(group + 1);
    ++n;
  }
  vector<vector> binom(n + 1);
  for (int i = 0; i <= n; ++i) {
    binom[i].push_back(1);
    for (int j = 1; j <= i; ++j) {
      binom[i].push_back((j != i ? binom[i - 1][j] : 0) + binom[i - 1][j - 1]);
    }
  }
  vector<vector>> dp(n, vector> (n));
  for (int j = 0; j < n; ++j) {
    for (int i = j; ~i; --i) {
      for (int k = i; k <= j; ++k) {
        pair value = make_pair(0, binom[j - i][k - i]);
        value = value * make_pair(a[k] == group + 1 ? 0 : sum(a[k]) - sum(i ? a[i - 1] : 0), min(a[k], group) - (i ? min(a[i - 1], group) : 0));
        if (k != i) {
          value = value * dp[i][k - 1];
        }
        if (k != j) {
          value = value * dp[k + 1][j];
        }
        dp[i][j] = dp[i][j] + value;
      }
    }
  }
  vector<vector>> f(hours, vector> (n)), sum(hours, vector> (n));
  for (int i = 0; i < hours; ++i) {
    for (int j = 0; j <= n + i - hours; ++j) {
      f[i][j] = dp[j][j + hours - i - 1];
    }
  }
  for (int i = hours - 1; ~i; --i) {
    for (int j = n - 1; ~j; --j) {
      for (int k = i + 1; k < hours; ++k) {
        if (j + k - i + 1 < n) {
          pair value = make_pair(0, binom[hours - i][hours - k]);
          value = value * dp[j][j + k - i - 1] * sum[k][j + k - i + 1];
          f[i][j] = f[i][j] + value;
        }
      }
      sum[i][j] = f[i][j];
      if (j + 1 < n) {
        sum[i][j] = sum[i][j] + sum[i][j + 1];
      }
    }
  }
  pair answer;
  for (int i = 0; i < n; ++i) {
    answer = answer + f[0][i];
  }
  printf("%.8lf\n", (double)(answer.first / answer.second));
  return 0;
}

Factor-Free Tree

分解质因数求出每个向左向右最远到哪都和它互质,按中序遍历考虑整个序列,维护一个栈表示之前的最右链,每次新增一个点的时候先往下挂,然后试图往上,直到父亲的 r r 大于自己的 r r 或者它的 l l 不能覆盖左边的子树。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  int m = *max_element(a.begin(), a.end());
  vector<int> min_prime(m + 1), primes;
  for (int i = 2; i <= m; ++i) {
    if (!min_prime[i]) {
      min_prime[i] = i;
      primes.push_back(i);
    }
    for (auto p : primes) {
      if (i * p > m) {
        break;
      }
      min_prime[i * p] = p;
      if (i % p == 0) {
        break;
      }
    }
  }
  vector<int> l(n), r(n), last(m + 1, -1);
  for (int i = 0; i < n; ++i) {
    l[i] = -1;
    int x = a[i];
    while (x != 1) {
      int p = min_prime[x];
      l[i] = max(l[i], last[p]);
      last[p] = i;
      while (x % p == 0) {
        x /= p;
      }
    }
    ++l[i];
  }
  for (int i = 0; i <= m; ++i) {
    last[i] = n;
  }
  for (int i = n - 1; ~i; --i) {
    r[i] = n;
    int x = a[i];
    while (x != 1) {
      int p = min_prime[x];
      r[i] = min(r[i], last[p]);
      last[p] = i;
      while (x % p == 0) {
        x /= p;
      }
    }
    --r[i];
  }
  vector<int> answer(n);
  vectorint, int>, int>> stack(n + 1);
  stack[0].first.second = -1;
  int top = 0;
  for (int i = 0; i < n; ++i) {
    stack[top + 1].first.second = -1;
    int temp = i;
    while (top && l[i] <= stack[top].first.first && r[i] >= stack[top].second) {
      temp = min(temp, stack[top].first.first);
      --top;
    }
    if (~stack[top + 1].first.second) {
      answer[stack[top + 1].first.second] = i;
    }
    answer[i] = stack[top].first.second;
    int right = top ? stack[top].second : n;
    if (right < i) {
      puts("impossible");
      return 0;
    }
    stack[++top] = make_pair(make_pair(temp, i), min(r[i], right));
  }
  for (int i = 0; i < n; ++i) {
    printf("%d%c", answer[i] + 1, i == n - 1 ? '\n' : ' ');
  }
  return 0;
}

Glyph Recognition

枚举边数,可以推公式算出两个bound,直接计算就行了。

#include 

using namespace std;

typedef long long ll;
typedef long double ld;

const ld pi = acosl(-1);
const ld inf = 1e9;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> x(n), y(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d %d", &x[i], &y[i]);
  }
  ld answer = 0;
  int best = -1;
  for (int i = 3; i <= 8; ++i) {
    ld inner = inf, outer = 0;
    for (int j = 0; j < n; ++j) {
      ld angle = atan2(y[j], x[j]);
      while (angle < 0) {
        angle += 2 * pi / i;
      }
      while (angle >= 2 * pi / i) {
        angle -= 2 * pi / i;
      }
      ld dist = sqrtl((ll)x[j] * x[j] + (ll)y[j] * y[j]) * cosl(angle - pi / i);
      inner = min(inner, dist);
      outer = max(outer, dist);
    }
    ld ratio = inner / outer;
    ratio *= ratio;
    if (ratio > answer) {
      answer = ratio;
      best = i;
    }
  }
  printf("%d %.8lf\n", best, (double)answer);
  return 0;
}

High Score

平方增长较快,所以枚举较小的两个增加了多少,这个值不会太大,暴力即可。

#include 

using namespace std;

typedef long long ll;

ll calc(int a, int b, int c) {
  return (ll)a * a + (ll)b * b + (ll)c * c + 7ll * min(a, min(b, c));
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int T;
  scanf("%d", &T);
  while (T--) {
    int a, b, c, d;
    scanf("%d %d %d %d", &a, &b, &c, &d);
    ll answer = 0;
    for (int i = 0; i <= d && i <= 100; ++i) {
      for (int j = 0; i + j <= d && j <= 100; ++j) {
        int k = d - i - j;
        answer = max(answer, calc(a + i, b + j, c + k));
        answer = max(answer, calc(a + i, b + k, c + j));
        answer = max(answer, calc(a + k, b + i, c + j));
      }
    }
    printf("%lld\n", answer);
  }
  return 0;
}

Installing Apps

考虑如果所有都选,最优顺序一定是按照 max(d,s)d max ( d , s ) − d 从大到小排序,然后记 f(i,j) f ( i , j ) 表示考虑前 i i 个物品,选了 j j 个,最少占用多少空间。

#include 

using namespace std;

struct info_t {
  int need, get, id;

  info_t(int need = 0, int get = 0, int id = 0):need(need), get(get), id(id) {
  }

  bool operator < (const info_t &b) const {
    return get > b.get;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, size;
  scanf("%d %d", &n, &size);
  vector apps(n);
  for (int i = 0; i < n; ++i) {
    int disk, storage;
    scanf("%d %d", &disk, &storage);
    apps[i] = info_t(max(disk, storage), max(disk, storage) - storage, i);
  }
  sort(apps.begin(), apps.end());
  vector<vector<int>> f(n + 1, vector<int> (n + 1, size + 1));
  vector<vector<bool>> g(n + 1, vector<bool> (n + 1, false));
  f[0][0] = 0;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j <= i; ++j) {
      f[i + 1][j] = f[i][j];
    }
    for (int j = 0; j <= i; ++j) {
      if (f[i][j] + apps[i].need <= size && f[i + 1][j + 1] > f[i][j] + apps[i].need - apps[i].get) {
        f[i + 1][j + 1] = f[i][j] + apps[i].need - apps[i].get;
        g[i + 1][j + 1] = true;
      }
    }
  }
  for (int i = n; ~i; --i) {
    if (f[n][i] <= size) {
      printf("%d\n", i);
      if (i) {
        vector<int> answer;
        for (int j = n, current = i; j; --j) {
          if (g[j][current]) {
            --current;
            answer.push_back(apps[j - 1].id);
          }
        }
        reverse(answer.begin(), answer.end());
        for (int j = 0; j < answer.size(); ++j) {
          printf("%d%c", answer[j] + 1, j == answer.size() - 1 ? '\n' : ' ');
        }
      }
      return 0;
    }
  }
  return 0;
}

Juggling Troupe

每个 2 2 单独考虑,假设它是 i i ,求出它左右的 0 0 的位置 l,r l , r ,那么就是将 l,r l , r 赋为 1 1 ,将 l+ri l + r − i 赋为 0 0 。用set维护即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  string str;
  cin >> str;
  int n = str.length();
  set<int> s;
  s.insert(-1);
  s.insert(n);
  for (int i = 0; i < n; ++i) {
    if (str[i] == '0') {
      s.insert(i);
    }
  }
  for (int i = 0; i < n; ++i) {
    if (str[i] == '2') {
      auto l = s.lower_bound(i), r = l;
      --l;
      int x = *l + *r - i;
      if (~*l) {
        s.erase(l);
      }
      if (*r < n) {
        s.erase(r);
      }
      s.insert(x);
    }
  }
  string answer(n, '1');
  for (auto p : s) {
    if (p >= 0 && p < n) {
      answer[p] = '0';
    }
  }
  cout << answer << endl;
  return 0;
}

Knockout Tournament

a1 a 1 认为是 0 0 ,按照 a a 从小到大排序,相邻的匹配最优。然后做个树形DP即可。

#include 

using namespace std;

typedef long double ld;

void dfs(int x, int m, int &index, vector<vectorint, ld>>> &f) {
  if ((x << 1 | 1) >= m) {
    f[x].push_back(make_pair(--index, 1));
  } else {
    dfs(x << 1 | 1, m, index, f);
    dfs(x + 1 << 1, m, index, f);
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  int first = a[0];
  a[0] = 0;
  sort(a.begin(), a.end());
  a[0] = first;
  int m = (n << 1) - 1, binary = 1;
  while ((binary << 1 | 1) <= m) {
    binary = binary << 1 | 1;
  }
  vector<vectorint, ld>>> f(m);
  int index = n;
  dfs(0, m, index, f);
  for (int i = m - n - 1, j = 0; ~i; --i) {
    int l = i << 1 | 1, r = i + 1 << 1;
    for (int rotate = 0; rotate < 2; ++rotate) {
      for (auto p : f[l]) {
        int index = p.first;
        ld prob = 0;
        for (auto q : f[r]) {
          prob += q.second * a[index] / (a[index] + a[q.first]);
        }
        f[i].push_back(make_pair(index, prob * p.second));
      }
      swap(l, r);
    }
  }
  for (auto p : f[0]) {
    if (!p.first) {
      printf("%.8lf\n", (double)p.second);
    }
  }
  return 0;
}

你可能感兴趣的:(CodeForces Gym 101623 简要题解)