AtCoder Beginner Contest 263

咕咕咕咕。

E - Sugoroku 3

反着跑DP,或者说逆向归纳。

记从\(i\)开始走到\(n\)的期望步数为\(dp_i\)。易得\(dp_n = 0\),然后\(dp_i\)可以由\(dp_{j}, i + 1 \le j \le i + a_i\)推出,从后往前推即可算出\(dp_1\),也就是答案。

具体就是假设摇骰子摇到\(x\),那么就可以花\(1\)步走到\(dp_{i + x}\),而\(dp_{i + x}\)是已经解决了的子问题,摇到\(x\)的概率恒为\(\frac{1}{a_i + 1}\),由此可得

\[dp_{i} = 1 + \frac{1}{a_i + 1}\sum_{j = i}^{j + a_i} dp_j \]

解得

\[dp_{i} = \frac{\sum_{j = i + 1}^{i + a_i} dp_j + a_i + 1}{a_i} \]

朴素的DP是\(O(n^2)\)的会超时,但是式子中求和那部分可以后缀和优化一下,然后就能做到\(O(n)\)

AC代码
// Problem: E - Sugoroku 3
// Contest: AtCoder - LINE  Verda Programming Contest(AtCoder Beginner Contest 263)
// URL: https://atcoder.jp/contests/abc263/tasks/abc263_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

template <typename ValueType, ValueType mod_, typename SupperType = int64_t>
class Modular {
 private:
  ValueType value_;

  ValueType normalize(ValueType value) const {
    if (value >= 0 && value < mod_)
      return value;
    value %= mod_;
    if (value < 0)
      value += mod_;
    return value;
  }

  ValueType power(ValueType value, size_t exponent) const {
    ValueType result = 1;
    ValueType base = value;
    while (exponent) {
      if (exponent & 1)
        result = SupperType(result) * base % mod_;
      base = SupperType(base) * base % mod_;
      exponent >>= 1;
    }
    return result;
  }

 public:
  Modular() : value_(0) {}

  Modular(const ValueType& value) : value_(normalize(value)) {}

  ValueType value() const { return value_; }

  Modular inv() const { return Modular(power(value_, mod_ - 2)); }

  Modular power(size_t exponent) const { return Modular(power(value_, exponent)); }

  friend Modular operator+(const Modular& lhs, const Modular& rhs) {
    ValueType result = lhs.value() + rhs.value() >= mod_ ? lhs.value() + rhs.value() - mod_
                                                         : lhs.value() + rhs.value();
    return Modular(result);
  }

  friend Modular operator-(const Modular& lhs, const Modular& rhs) {
    ValueType result = lhs.value() - rhs.value() < 0 ? lhs.value() - rhs.value() + mod_
                                                     : lhs.value() - rhs.value();
    return Modular(result);
  }

  friend Modular operator*(const Modular& lhs, const Modular& rhs) {
    ValueType result = SupperType(1) * lhs.value() * rhs.value() % mod_;
    return Modular(result);
  }

  friend Modular operator/(const Modular& lhs, const Modular& rhs) {
    ValueType result = SupperType(1) * lhs.value() * rhs.inv().value() % mod_;
    return Modular(result);
  }
};
template <typename StreamType, typename ValueType, ValueType mod, typename SupperType = int64_t>
StreamType& operator<<(StreamType& out, const Modular<ValueType, mod, SupperType>& modular) {
  return out << modular.value();
}
template <typename StreamType, typename ValueType, ValueType mod, typename SupperType = int64_t>
StreamType& operator>>(StreamType& in, Modular<ValueType, mod, SupperType>& modular) {
  ValueType value;
  in >> value;
  modular = Modular<ValueType, mod, SupperType>(value);
  return in;
}
// using Mint = Modular<int, 1'000'000'007>;
using Mint = Modular<int, 998'244'353>;

class Binom {
 private:
  std::vector<Mint> f, g;

 public:
  Binom(int n) {
    f.resize(n + 1);
    g.resize(n + 1);

    f[0] = Mint(1);
    for (int i = 1; i <= n; ++i)
      f[i] = f[i - 1] * Mint(i);
    g[n] = f[n].inv();
    for (int i = n - 1; i >= 0; --i)
      g[i] = g[i + 1] * Mint(i + 1);
  }
  Mint operator()(int n, int m) {
    if (n < 0 || m < 0 || m > n)
      return Mint(0);
    return f[n] * g[m] * g[n - m];
  }
};

void solve_case(int Case) {
  int n;
  std::cin >> n;

  std::vector<int> a(n + 1);
  for (int i = 1; i <= n - 1; ++i)
    std::cin >> a[i];

  std::vector<Mint> dp(n + 1, Mint(0));
  std::vector<Mint> s(n + 2, Mint(0));
  for (int i = n - 1; i >= 1; --i) {
    Mint S = (s[i + 1] - s[i + a[i] + 1]);
    dp[i] = (S + Mint(a[i] + 1)) / Mint(a[i]);
    s[i] = s[i + 1] + dp[i];
    logd(i, s[i + 1].value(), s[i + a[i] + 1].value(), S.value(), dp[i].value());
  }
  std::cout << dp[1].value() << "\n";
}

F - Tournament

DP。

可以把赛程图看成一个满二叉树,从底下往上对层从\(0\)开始编号。

对于某个选手\(j\),假设他一直打到了第\(i\)层,则这层中的比赛他能够参加的只有一个。

对于第\(i\)层的某场比赛,这场比赛可能的参赛选手有\(2^i\)个,且这场比赛是由前一半的选手之一和后一半的选手之一来打。

假设\(dp_{i, j}\)表示\(i\)层的比赛中,\(j\)能参加的那场比赛的胜者为\(j\),以这场比赛为根的子树中所有比赛的最大收益之和。\(dp_{n, j}\)即为最终胜者为\(j\)的收益,由此答案即为\(\max_j dp_{n, j}\)

\(dp_{i, j}\)其实是对应一个二叉树,可以由根节点以及根节点的两棵子树加起来得到。

假设这场比赛的胜者为来自前一半的\(x\),负者为来自后一半的\(y\),那么这场比赛对应收益\(dp_{i, x} = dp_{i - 1, x} + c_{x, i} - c_{x, i - 1} + \max_y dp_{i - 1, y}\)。其中\(c_{x, i} - c_{x, i - 1}\)为这场比赛的收益,剩余为前\(i - 1\)层中的比赛的收益。

分别枚举\(x\)\(y\)即可算出\(dp_{i, x}\)但是会超时。观察可得\(x\)的改变并不会影响\(\max_y dp_{i - 1, y}\),所以可以先把\(\max_y dp_{i - 1, y}\)算出来当常数用,后续就不用重复再算了。

类似地可以求出\(dp_{i, y}\)

然后\(dp_{i}\)只和\(dp_{i - 1}\)有关,所以可以搞滚动数组优化节省空间。

AC代码
// Problem: F - Tournament
// Contest: AtCoder - LINE  Verda Programming Contest(AtCoder Beginner Contest 263)
// URL: https://atcoder.jp/contests/abc263/tasks/abc263_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

void solve_case(int Case) {
  int n;
  std::cin >> n;
  int m = (1 << n);

  std::vector<std::vector<i64>> c(m, std::vector<i64>(n + 1, 0));
  for (int i = 0; i < m; ++i) {
    for (int j = 1; j <= n; ++j) {
      std::cin >> c[i][j];
    }
  }

  std::vector<i64> dp(m, 0), temp;
  for (int i = 1; i <= n; ++i) {
    temp = dp;
    for (int j = 0; j < m; j += (1 << i)) {
      i64 lmx = *std::max_element(temp.begin() + j, temp.begin() + j + (1 << (i - 1)));
      i64 rmx = *std::max_element(temp.begin() + j + (1 << (i - 1)), temp.begin() + j + (1 << i));
      for (int k = j; k < j + (1 << (i - 1)); ++k) {
        dp[k] = temp[k] + (c[k][i] - c[k][i - 1]) + rmx;
      }
      for (int k = j + (1 << (i - 1)); k < j + (1 << i); ++k) {
        dp[k] = temp[k] + (c[k][i] - c[k][i - 1]) + lmx;
      }
    }
  }

  i64 ans = *std::max_element(dp.begin(), dp.end());
  std::cout << ans << "\n";
}

G - Erasing Prime Pairs

网络流。

先不考虑存在\(a_i = 1\)的情况,则此时由两个数加起来能够得到的素数都大于\(2\),所以都是奇数,所以可以拆成奇数加偶数,把奇数和偶数分成两部分,然后就是二分图最大匹配了,可以最大流跑。

现在考虑存在\(a_i = 1\)的情况,这个时候有一种可能就是\(1 + 1 = 2\)搞出来偶素数,这个就比较麻烦了。

\(f(x)\)表示跑了\(x\)\(1 + 1\),那么可以证明\(f(x) + x\)是单峰的。具体就是其相邻两项的差值为一段\(+1\),一段\(0\),一段\(-1\)\(+1\)对应有多余的\(1\)两两匹配;\(0\)对应原本有\(1+x\)\(1+y\)两个匹配,现在变成了\(1+1\)\(x+y\)两个匹配;\(-1\)对应原本有\(1+x\)\(1+y\)两个匹配,现在\(x\)\(y\)匹配不上变成了只有\(1+1\)这个匹配。

\(f(x) + x\)的图像画出来,观察可得根据最左边的点和最右边的点,就可以推出极值的下标。过程大概就是找出另外一个点使得\(f(a) = f(0)\),这个根据最后一段斜率为\(-1\)就可以得到,再然后就是\(\frac{a}{2}\)处一定取极值。

AC代码
// Problem: G - Erasing Prime Pairs
// Contest: AtCoder - LINE  Verda Programming Contest(AtCoder Beginner Contest 263)
// URL: https://atcoder.jp/contests/abc263/tasks/abc263_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

const int INF = 0x3f3f3f3f;

namespace PollardRho {

static std::mt19937_64 rng_(std::chrono::steady_clock::now().time_since_epoch().count());

inline int64_t Rand(int64_t l, int64_t r) {
  return l + rng_() % (r - l + 1);
}

inline int64_t Add(int64_t a, int64_t b, int64_t mod) {
  return ((__int128_t)a + b) % mod;
}

inline int64_t Substract(int64_t a, int64_t b, int64_t mod) {
  return (((__int128_t)a - b) % mod + mod) % mod;
}

inline int64_t Multiply(int64_t a, int64_t b, int64_t mod) {
  return (__int128_t)a * b % mod;
}

inline int64_t Power(int64_t a, int64_t b, int64_t mod) {
  int64_t r = 1;
  while (b) {
    if (b & 1)
      r = Multiply(r, a, mod);
    a = Multiply(a, a, mod);
    b >>= 1;
  }
  return r;
}

// Time Complexity: $O(k \log^{3} n)$
bool MillerRabinTest(int64_t n) {
  // Strong enough for $n < 2^64$, see https://oeis.org/A014233.
  constexpr static int kTestRounds = 12;
  constexpr static int kTestBase[kTestRounds] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

  if (n <= kTestBase[kTestRounds - 1]) {
    return *std::lower_bound(kTestBase, kTestBase + kTestRounds, n) == n;
  }

  int64_t d = n - 1, r = 0;
  while (d % 2 == 0) {
    d >>= 1;
    r = r + 1;
  }

  for (int round = 0; round < kTestRounds; ++round) {
    int64_t a = kTestBase[round];

    // Fermet primality test.
    int64_t x = Power(a, d, n);
    if (x == 1 || x == n - 1)
      continue;

    // Witness primality test.
    for (int i = 0; i < r - 1; ++i) {
      x = Multiply(x, x, n);
      if (x == n - 1)
        break;
    }
    if (x != n - 1)
      return false;
  }

  return true;
}

int64_t Rho(int64_t n) {
  // Can not factor 4 because the faster step gap is 2.
  if (n == 4)
    return 2;

  const static int kMaxStepSize = 1 << 7;

  int64_t c;
  std::function<int64_t(int64_t)> f = [&n, &c](int64_t x) { return Add(Multiply(x, x, n), c, n); };

  for (;;) {
    c = Rand(3, n - 1);

    int64_t t = f(Rand(0, n - 1)), r = f(t);
    for (int goal = 1; t != r; goal = std::min(goal << 1, kMaxStepSize)) {
      int64_t d1, d2 = 1;

      for (int step = 0; step < goal; ++step) {
        d1 = Multiply(d2, Substract(t, r, n), n);
        if (d1 == 0)
          break;
        d2 = d1;
        t = f(t);
        r = f(f(r));
      }

      int64_t d = std::gcd(d2, n);
      if (d != 1 and d != n)
        return d;
    }
  }
}

std::vector<int64_t> Factor(int64_t n) {
  std::vector<int64_t> factors;
  std::function<void(int64_t)> factor = [&](int64_t n) {
    if (n < 2)
      return;

    if (MillerRabinTest(n)) {
      factors.push_back(n);
    } else {
      int64_t x = Rho(n);
      while (n % x == 0)
        n /= x;
      factor(x);
      factor(n);
    }
  };
  factor(n);
  std::sort(factors.begin(), factors.end());
  return factors;
}

};  // namespace PollardRho

template <typename CapacityType>
class MaxFlowGraph {
  struct Edge {
    int from, to;
    CapacityType capacity, flow;
    Edge() {}
    Edge(int _from, int _to, CapacityType _capacity, CapacityType _flow)
        : from(_from), to(_to), capacity(_capacity), flow(_flow) {}
  };

  int n_;
  int m_;
  std::vector<Edge> edges_;
  std::vector<std::vector<int>> adjacent_;

 public:
  explicit MaxFlowGraph(int n) : n_(n), m_(0), edges_(0), adjacent_(n) {}

  void AddEdge(int from, int to, CapacityType capacity) {
    assert(0 <= from and from < n_);
    assert(0 <= to and to < n_);

    edges_.emplace_back(from, to, capacity, 0);
    adjacent_[from].push_back(m_);
    ++m_;

    edges_.emplace_back(to, from, 0, 0);
    adjacent_[to].push_back(m_);
    ++m_;
  }

  CapacityType Dinic(int src, int dst) {
    const static CapacityType INF = std::numeric_limits<CapacityType>::max();
    std::vector<int> level(n_);
    std::vector<int> start_index(n_);

    std::function<bool()> bfs = [&]() -> bool {
      std::fill(level.begin(), level.end(), -1);

      std::queue<int> q;
      q.push(src);
      level[src] = 0;

      while (!q.empty()) {
        int u = q.front();
        q.pop();

        for (int edge_id : adjacent_[u]) {
          auto [from, to, capacity, flow] = edges_[edge_id];
          CapacityType residual_capacity = capacity - flow;
          if (residual_capacity > 0 && level[to] == -1) {
            level[to] = level[u] + 1;
            if (to == dst)
              break;
            q.push(to);
          }
        }
      }

      return level[dst] != -1;
    };

    std::function<CapacityType(int, CapacityType)> dfs =
        [&](int u, CapacityType max_augment) -> CapacityType {
      if (u == dst)
        return max_augment;

      if (max_augment == 0)
        return 0;

      CapacityType total_augment = 0;
      int i = start_index[u];
      for (; i < (int)adjacent_[u].size(); ++i) {
        int edge_id = adjacent_[u][i];
        auto [from, to, capacity, flow] = edges_[edge_id];
        if (level[to] == level[u] + 1) {
          CapacityType residual_capacity = capacity - flow;
          CapacityType new_augment = dfs(to, std::min(max_augment, residual_capacity));
          if (new_augment <= 0)
            continue;

          max_augment -= new_augment;
          total_augment += new_augment;
          edges_[edge_id].flow += new_augment;
          edges_[edge_id ^ 1].flow -= new_augment;

          if (max_augment == 0)
            break;
        }
      }
      start_index[u] = i;

      if (total_augment == 0)
        level[u] = -1;

      return total_augment;
    };

    CapacityType max_flow = 0;
    while (bfs()) {
      std::fill(start_index.begin(), start_index.end(), 0);
      CapacityType new_flow = dfs(src, INF);
      logd(new_flow);
      max_flow += new_flow;
    }

    return max_flow;
  }
};

void solve_case(int Case) {
  int n;
  std::cin >> n;

  std::vector<std::pair<int, int>> v0, v1;
  int c1 = 0;
  for (int i = 0; i < n; ++i) {
    int a, b;
    std::cin >> a >> b;
    if (a & 1) {
      v1.push_back({a, b});
    } else {
      v0.push_back({a, b});
    }
    if (a == 1) {
      c1 = b;
    }
  }

  auto f = [&](int k) {
    i64 ans = 0;
    MaxFlowGraph<i64> g(n + 2);
    int S = n, T = S + 1;
    for (int i = 0; i < v0.size(); ++i) {
      auto [a, b] = v0[i];
      g.AddEdge(S, i, b);
    }
    for (int i = 0; i < v1.size(); ++i) {
      auto [a, b] = v1[i];
      if (a == 1) {
        g.AddEdge(v0.size() + i, T, b - 2 * k);
      } else {
        g.AddEdge(v0.size() + i, T, b);
      }
    }
    for (int i = 0; i < v0.size(); ++i) {
      for (int j = 0; j < v1.size(); ++j) {
        auto [x, _0] = v0[i];
        auto [y, _1] = v1[j];
        if (PollardRho::MillerRabinTest(x + y)) {
          g.AddEdge(i, v0.size() + j, INF);
        }
      }
    }
    return g.Dinic(S, T) + k;
  };

  i64 y0 = f(0), y1 = f(c1 / 2);
  i64 x = (c1 / 2 - (y0 - y1)) / 2;
  std::cout << f(x) << "\n";
}

Ex - Intersection 2

To be solved。

posted @ 2022-08-12 23:58  _Backl1ght  阅读(102)  评论(0编辑  收藏  举报