AtCoder Beginner Contest 280

D - Factorial and Multiple

\(k\) 进行质因数分解。

如果 \(k\) 最大的质因子 \(p\) 满足 \(p * p > k\) ,那么答案就是 \(p\)。因为一定要包含一次,也只需要包含一次。

否则直接爆搜。

AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"

#include "hira/math/prime/pollard_rho.h"

void Initialize() {}

void SolveCase(int Case) {
  i64 k;
  std::cin >> k;

  auto p = math::Factor(k);
  logd(p);
  if (p.back() > 10'000'000) {
    std::cout << p.back() << "\n";
  } else {
    for (int i = 1;; ++i) {
      int g = std::gcd(i, k);
      k /= g;
      if (k == 1) {
        std::cout << i << "\n";
        break;
      }
    }
  }
}

E - Critical Hit

枚举用了 \(i\)\(-1\),那么用了多少个 \(-2\) 也就能算出来了,记用了 \(j\)\(-2\)

然后,如果 \(i + 2j > n\) ,说明最后一个操作为 \(2\) ,否则最后一个操作任意。

然后就是算一下概率,然后套期望公式了。

递推的做法:\(f(0) = 0, f(1) = 1, f(i) = pf(i - 2) + qf(i-1) + 1\)

AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"

#include "hira/math/modular/binom.h"
#include "hira/math/modular/mod_int.h"

using mint = math::ModInt998;
const auto binom = math::binom<mint>;

void Initialize() {}

void SolveCase(int Case) {
  int n, v;
  std::cin >> n >> v;

  mint p = mint(100 - v) / 100, q = mint(v) / 100;

  mint ans = 0;
  for (int i = 0; i <= n; ++i) {
    int j = (n - i + 1) / 2;

    if (i + 2 * j > n)
      ans += mint(i + j) * p.power(i) * q.power(j) * binom(i + j - 1, i);
    else
      ans += mint(i + j) * p.power(i) * q.power(j) * binom(i + j, i);

    logd(i, j, ans);
  }

  std::cout << ans.value() << "\n";
}

F - Pay or Receive

如果不属于同一连通块,就是 nan ,这个可以用并查集搞。

否则,如果该连通块有权非零的环,则 inf,正环就不解释了,在本题中负环反着跑就是正环。这个的判断可以跑遍 DFS。

现在需要回答多源多汇最长路的询问,普适的算法必定超时,所以要根据题目条件进一步优化。

现在没有权非零的环了,任意两点间的距离是唯一的,因为如果不唯一就能推出有权非零的环。

所以可以转成树上的两点间距离询问,经典。

还可以进一步优化:对于任意两点,\(d(x, y) = -d(y,x)\) ,假设以 \(r\) 为根跑出 DFS 树,那么 \(d(r, x) + d(x, y) + d(y, r) = 0, d(x, y) = -d(r, x) - d(y, r) = d(r, y) - d(r, x)\)

AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"

#include "hira/ds/dsu.h"

void Initialize() {}

void SolveCase(int Case) {
  int n, m, q;
  std::cin >> n >> m >> q;

  ds::DSU d(n);
  std::vector<std::vector<std::pair<i64, int>>> g(n);
  for (int i = 0; i < m; ++i) {
    int a, b, c;
    std::cin >> a >> b >> c;
    --a, --b;

    d.merge(a, b);
    g[a].push_back({c, b});
    g[b].push_back({-c, a});
  }

  const i64 INF = INT64_C(0x3f3f3f3f3f3f3f3f);
  std::vector<i64> dist(n, -INF);
  std::vector<bool> has_positive_loop(n, false);
  for (int i = 0; i < n; ++i) {
    if (d.leader(i) != i)
      continue;

    std::queue<int> q;
    q.push(i);
    dist[i] = 0;
    while (!q.empty()) {
      int u = q.front();
      q.pop();

      for (auto [w, v] : g[u]) {
        if (dist[v] == -INF) {
          dist[v] = dist[u] + w;
          q.push(v);
        } else {
          if (dist[v] != dist[u] + w)
            has_positive_loop[i] = true;
        }
      }
    }
  }

  for (int i = 0; i < q; ++i) {
    int a, b;
    std::cin >> a >> b;
    --a, --b;

    if (!d.same(a, b)) {
      std::cout << "nan\n";
      continue;
    }

    if (has_positive_loop[d.leader(a)]) {
      std::cout << "inf\n";
      continue;
    }

    std::cout << -dist[a] + dist[b] << "\n";
  }
}

G - Do Use Hexagon Grid 2

在这种六角网格上,从 \((0, 0)\) 走到 \((x, y)\) 的距离为 \(\max(|x|, |y|, |x - y|)\)

证明

感性的理解,六角网格是在四角网格的基础上,新增了右上和左下两个方向的移动。
对于右上方向和左下方向,横纵座标可以同时改变,其中一个到达目标值之后再仅改变另外一个。由此距离为 \(\max(|x|, |y|)\)
对于左上和右下,只能先改一个座标,再改另一个座标,由此距离为 \(|x| + |y| = |x - y|\)
综合一下就是距离为 \(\max(|x|, |y|, |x - y|)\)

然后就可以把六角网格座标转换成三维座标,把距离看成三维座标系中的 Chebyshev 距离。

现在问题转换成有多少个点集满足点集中的点位于某一个 \(d \times d \times d\) 的正方体中。

为了防止重复计数,依次计算满足以下条件的点集 \(S\) 数量,然后再求和。(不断移动正方体,一旦移动一定会有某些点不再位于正方体中,从而达到去重的目的)

  • \(\min_{(x_i, y_i, z_i) \in S x_i} = x\)
  • \(\min_{(x_i, y_i, z_i) \in S y_i} = y\)
  • \(\min_{(x_i, y_i, z_i) \in S z_i} = z\)

将点集中的点 \((x_i, y_i, z_i)\) 分成 \(8\) 类:

  • \(x_i\) 是否为 \(x\)
  • \(y_i\) 是否为 \(y\)
  • \(z_i\) 是否为 \(z\)

假设集合名中出现 \(X\) 就表示满足 \(x_i = x\) 的点, \(Y\)\(Z\) 同理。出现多个大写字母就是多个点集的交。三个座标都不满足的点集为 \(O\)

再手动去重算点集数量。

  • 先将点集分成包含 \(XYZ\) 中点的和不包含的,包含了 \(XYZ\) 中点之后其他点都可以任意选,所以这一类数量为 \((2^{|XYZ|} - 1) 2^{|O| + |Z| + |Y| + |YZ| + |X| + |XZ| + |XY|}\)
  • 此后都不能包含 \(XYZ\) 了,将点分为包含 \(XY\) 中点的和不包含的,,包含了 \(XY\) 中的还要再 \(Z \cup YZ \cup XZ\) 中至少选一个点,然后 \(O \cup Y \cup X\) 中的点可以任选。
  • 以此类推。
AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"

#include "hira/math/modular/mod_int.h"
using mint = math::ModInt998;

#include "hira/std_extension/discretize.h"

void Initialize() {}

void SolveCase(int Case) {
  int n;
  i64 d;
  std::cin >> n >> d;

  std::vector<mint> p2(n + 1);
  p2[0] = 1;
  for (int i = 1; i <= n; ++i)
    p2[i] = p2[i - 1] * 2;

  std::vector<std::array<int, 3>> a(n);
  std::vector<int> xx(n), yy(n), zz(n);
  for (int i = 0; i < n; ++i) {
    int x, y, z;
    std::cin >> x >> y;
    z = x - y;

    a[i] = {x, y, z};
    xx[i] = x;
    yy[i] = y;
    zz[i] = z;
  }
  std::sort(
      a.begin(), a.end(),
      [](const std::array<int, 3>& lhs, const std::array<int, 3>& rhs) -> bool {
        return lhs[0] < rhs[0];
      });

  xx = stdext::discretize(xx);
  yy = stdext::discretize(yy);
  zz = stdext::discretize(zz);

  auto calc = [&](const std::array<int, 8>& c) {
    auto [none, z, y, yz, x, xz, xy, xyz] = c;

    mint ret(0);

    ret += (p2[xyz] - 1) * p2[none + z + y + yz + x + xz + xy];
    ret += (p2[xy] - 1) * (p2[z + yz + xz] - 1) * p2[y + x + none];
    ret += (p2[xz] - 1) * (p2[y + yz] - 1) * p2[z + x + none];
    ret += (p2[x] - 1) * (p2[yz] - 1) * p2[none + z + y];
    ret += (p2[x] - 1) * (p2[y] - 1) * (p2[z] - 1) * p2[none];

    return ret;
  };

  std::vector<std::array<int, 3>> p(n);
  auto solve = [&](int y, int z) {
    mint ret(0);

    int r = -1;
    std::array<int, 8> c = {0};

    int curr = 0;
    p.clear();

    for (int x : xx) {
      // previous points lies on left bound of x coordinate now invalid.
      // remove them.
      for (int t = 0; t < 8; ++t) {
        if (t & 0b100)
          c[t] = 0;
      }

      // new points fit in the range.
      while (r + 1 < n && a[r + 1][0] <= x + d) {
        ++r;
        if (a[r][1] < y || a[r][1] > y + d || a[r][2] < z || a[r][2] > z + d)
          continue;
        ++c[((a[r][1] == y) << 1) | (a[r][2] == z)];
        p.push_back(a[r]);
      }

      // update points lies on left bound of x coordinate.
      while (curr < p.size() && p[curr][0] == x) {
        --c[((p[curr][1] == y) << 1) | (p[curr][2] == z)];
        ++c[0b100 | ((p[curr][1] == y) << 1) | (p[curr][2] == z)];
        ++curr;
      }

      ret += calc(c);
    }

    return ret;
  };

  mint ans(0);
  for (int y : yy) {
    for (int z : zz) {
      ans += solve(y, z);
    }
  }

  std::cout << ans.value() << "\n";
}

Ex - Substring Sort

To be solved.

posted @ 2022-12-04 20:00  _Backl1ght  阅读(156)  评论(0编辑  收藏  举报