AtCoder Beginner Contest 265

E - Warp

注意到 \(N\) 相比 \(M\) 要小得多。

考虑 DP, 令 \(dp_{i, j, k}\) 表示一共使用了 \(i + j + k\) 次操作,且每种操作的使用次数分别为 \(i, j, k\) 的方案数,然后用 std::set 容器快速判断能否转移到下一个状态(下一个点上是否有障碍物),时间复杂度 \(O(N^3 \log M)\)

AC代码
// Problem: E - Warp
// Contest: AtCoder - AtCoder Beginner Contest 265
// URL: https://atcoder.jp/contests/abc265/tasks/abc265_e
// Memory Limit: 1024 MB
// Time Limit: 3000 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(...) ;
#define ASSERT(x) ;
#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, i64>;

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];
  }
} binom(300);

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

  i64 a, b, c, d, e, f;
  std::cin >> a >> b >> c >> d >> e >> f;

  std::set<std::pair<i64, i64>> s;
  for (int i = 0; i < m; ++i) {
    int x, y;
    std::cin >> x >> y;
    s.insert({x, y});
  }

  std::vector<std::vector<std::vector<Mint>>> dp(
      n + 1, std::vector<std::vector<Mint>>(n + 1, std::vector<Mint>(n + 1, Mint(0))));
  dp[0][0][0] = Mint(1);

  auto p = [&](int i, int j, int k) {
    i64 x = a * i + c * j + e * k;
    i64 y = b * i + d * j + f * k;
    return std::make_pair(x, y);
  };

  Mint ans(0);
  for (int i = 0; i <= n; ++i) {
    for (int j = 0; j <= n - i; ++j) {
      for (int k = 0; k <= n - i - j; ++k) {
        if (!s.count(p(i, j, k))) {
          if (i + j + k == n)
            ans = ans + dp[i][j][k];

          if (i + 1 <= n)
            dp[i + 1][j][k] = dp[i + 1][j][k] + dp[i][j][k];
          if (j + 1 <= n)
            dp[i][j + 1][k] = dp[i][j + 1][k] + dp[i][j][k];
          if (k + 1 <= n)
            dp[i][j][k + 1] = dp[i][j][k + 1] + dp[i][j][k];
        }
      }
    }
  }

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

F - Manhattan Cafe

首先有个朴素的DP,\(dp_{i, j, k}\) 表示考虑了前 \(i\) 维,然后 \(\sum_{x = 1}^i |p_x - r_x| = j, \sum_{x = 1}^i |q_x - r_x| = k\),每次转移需要枚举 \(j\)\(k\)\(r_i\) ,时间复杂度 \(O(ND^3)\),复杂度爆炸。

根据 \(r_x, p_x, q_x\) 的大小,可以将转移分成 3 类:

  • \(r_x\) 小于 \(\min(p_x, q_x)\)
  • \(\min(p_x, q_x) \le r_x \le \max(p_x, q_x)\)
  • \(r_x\) 大于 \(\min(p_x, q_x)\)

每一类都对应一条直线,斜率为 \(\plusmn 1\),而这些直线上的值都可以借助前缀和优化做到 \(O(N^2) \sim O(1)\),然后就可以将时间复杂度降低到 \(O(ND^2)\)

AC代码
// Problem: F - Manhattan Cafe
// Contest: AtCoder - AtCoder Beginner Contest 265
// URL: https://atcoder.jp/contests/abc265/tasks/abc265_f
// Memory Limit: 1024 MB
// Time Limit: 6000 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(...) ;
#define ASSERT(x) ;
#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);
  }

  std::string to_string() const { return std::to_string(value_); }
};
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, d;
  std::cin >> n >> d;

  std::vector<int> p(n), q(n);
  for (int i = 0; i < n; ++i)
    std::cin >> p[i];
  for (int i = 0; i < n; ++i)
    std::cin >> q[i];

  std::vector<std::vector<Mint>> dp(d + 1, std::vector<Mint>(d + 1, Mint(0)));
  dp[0][0] = Mint(1);

  for (int t = 0; t < n; ++t) {
    auto diag1 = dp;
    for (int i = 0; i <= d; ++i) {
      for (int j = 0; j <= d; ++j) {
        if (i - 1 >= 0 && j - 1 >= 0)
          diag1[i][j] = diag1[i][j] + diag1[i - 1][j - 1];
      }
    }

    auto diag2 = dp;
    for (int i = 0; i <= d; ++i) {
      for (int j = d; j >= 0; --j) {
        if (i - 1 >= 0 && j + 1 <= d)
          diag2[i][j] = diag2[i][j] + diag2[i - 1][j + 1];
      }
    }

    int s = std::abs(p[t] - q[t]);
    for (int i = 0; i <= d; ++i) {
      for (int j = 0; j <= d; ++j) {
        dp[i][j] = Mint(0);

        // i - j = s
        {
          int x = i - 1, y = j - s - 1;
          if (x >= 0 && y >= 0)
            dp[i][j] = dp[i][j] + diag1[x][y];
        }

        // i - j = -s
        int x = i - s - 1, y = j - 1;
        if (x >= 0 && y >= 0)
          dp[i][j] = dp[i][j] + diag1[x][y];

        // i + j = s
        int x1 = i, y1 = j - s;
        int x2 = i - s, y2 = j;

        if (y1 < 0) {
          x1 += y1;
          y1 = 0;
        }
        if (x1 >= 0) {
          dp[i][j] = dp[i][j] + diag2[x1][y1];
        }

        if (x2 - 1 >= 0 && y2 + 1 <= d) {
          dp[i][j] = dp[i][j] - diag2[x2 - 1][y2 + 1];
        }
      }
    }
  }

  Mint ans(0);
  for (int i = 0; i <= d; ++i)
    for (int j = 0; j <= d; ++j)
      ans = ans + dp[i][j];

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

G - 012 Inversion

经典线段树区间操作。

每个线段树节点维护:

  • \(a_x\):子树中 \(x\) 的个数,\(0 \le x \le 2\)
  • \(b_{x, y}\):子树中有序对 \((x, y)\) 的个数,\(0 \le x, y \le 2\)

操作一的答案就是 \(b_{1, 0} + b_{2, 0} + b_{2, 1}\)

操作二就是维护一下区间修改,假设现在的修改为 \(x \to f(x), 0 \le x \le 2\),那么\(a^\prime_{f(x)} = a^\prime_{f(x)} + a_{x}, b^\prime_{f(x), f(y)} = b^\prime_{f(x), f(y)} + b_{x, y}\)

AC代码
// Problem: G - 012 Inversion
// Contest: AtCoder - AtCoder Beginner Contest 265
// URL: https://atcoder.jp/contests/abc265/tasks/abc265_g
// Memory Limit: 1024 MB
// Time Limit: 5000 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(...) ;
#define ASSERT(x) ;
#define serialize(...) std::string("")
#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 Data, typename Tag>
class SegmentTree {
 public:
  struct Node {
    Node* left_child_;
    Node* right_child_;

    int left_bound_;
    int right_bound_;

    Data data_;
    Tag tag_;

    void ApplayUpdate(const Tag& tag) {
      data_.Apply(left_bound_, right_bound_, tag);
      tag_.Apply(left_bound_, right_bound_, tag);
    }

    void MaintainInfomation() {
      ASSERT(left_child_ && right_child_);

      data_ = left_child_->data_ + right_child_->data_;
    }

    void Propagation() {
      if (tag_.NeedPropagation()) {
        right_child_->ApplayUpdate(tag_);
        left_child_->ApplayUpdate(tag_);
        tag_.Reset();
      }
    }

    Node() : left_child_(nullptr), right_child_(nullptr), left_bound_(-1), right_bound_(-1) {}
  };

  /*
   * Used for binary search on segment tree.
   *
   * If it should go to the desire(or optimal) direction, then return ture. Otherwise return false.
   * For example, if you want to find the leftmost position satisfying some condition, then return
   * true to go left.
   */
  using Judger = std::function<bool(const Data&, const Data&)>;

 private:
  void UpdateInternal(Node* p, int left, int right, const Tag& tag) {
    ASSERT(p);

    if (p->left_bound_ >= left && p->right_bound_ <= right) {
      p->ApplayUpdate(tag);
      return;
    }

    p->Propagation();

    if (p->left_child_->right_bound_ >= left)
      UpdateInternal(p->left_child_, left, right, tag);
    if (p->right_child_->left_bound_ <= right)
      UpdateInternal(p->right_child_, left, right, tag);

    p->MaintainInfomation();
  }

  const Data QueryInternal(Node* p, int left, int right) {
    ASSERT(p);

    if (p->left_bound_ >= left && p->right_bound_ <= right)
      return p->data_;

    p->Propagation();

    Data result;
    if (p->left_child_->right_bound_ >= left)
      result = result + QueryInternal(p->left_child_, left, right);
    if (p->right_child_->left_bound_ <= right)
      result = result + QueryInternal(p->right_child_, left, right);

    return result;
  }

  std::pair<int, const Data> FindLeftmostIfInternal(Node* p, const Judger& judger) {
    ASSERT(p);

    if (p->left_bound_ == p->right_bound_)
      return {p->left_bound_, p->data_};

    p->Propagation();

    if (judger(p->left_child_->data_, p->right_child_->data_))
      return FindLeftmostIfInternal(p->left_child_, judger);
    return FindLeftmostIfInternal(p->right_child_, judger);
  }

  std::pair<int, const Data> FindRightmostIfInternal(Node* p, const Judger& judger) {
    ASSERT(p);

    if (p->left_bound_ == p->right_bound_)
      return {p->left_bound_, p->data_};

    p->Propagation();

    if (judger(p->left_child_->data_, p->right_child_->data_))
      return FindRightmostIfInternal(p->right_child_, judger);
    return FindRightmostIfInternal(p->left_child_, judger);
  }

 public:
  SegmentTree(const std::vector<Data>& array) : n_(array.size()) {
    std::function<Node*(int, int)> build = [&](int left, int right) -> Node* {
      Node* p = new Node();
      p->left_bound_ = left;
      p->right_bound_ = right;

      if (left == right) {
        p->data_ = array[left];
      } else {
        int middle = (left + right) >> 1;
        p->left_child_ = build(left, middle);
        p->right_child_ = build(middle + 1, right);
        p->MaintainInfomation();
      }

      return p;
    };

    root_ = build(0, n_ - 1);
  }

  ~SegmentTree() {
    std::function<void(Node*)> dfs = [&](Node* p) {
      if (!p)
        return;
      dfs(p->left_child_);
      dfs(p->right_child_);
      delete p;
    };

    dfs(root_);
  }

  void Update(int left, int right, const Tag& tag) {
    ASSERT(left >= 0 && right < n_);

    UpdateInternal(root_, left, right, tag);
  }

  const Data Query(int left, int right) {
    ASSERT(left >= 0 && right < n_);

    return QueryInternal(root_, left, right);
  }

  std::pair<int, const Data> FindLeftmostIf(const Judger& judger) {
    return FindLeftmostIfInternal(root_, judger);
  }

  std::pair<int, const Data> FindRightmostIf(const Judger& judger) {
    return FindRightmostIfInternal(root_, judger);
  }

  std::string to_string() const {
    std::stringstream ss;
    ss << "SegmentTree [\n";
    std::function<void(Node*)> dfs = [&](Node* p) {
      if (p->left_bound_ == p->right_bound_) {
        ss << "  [" << p->left_bound_ << "]: {" << p->data_.to_string() << "}, {"
           << p->tag_.to_string() << "}\n";
        return;
      }
      dfs(p->left_child_);
      dfs(p->right_child_);
    };
    dfs(root_);
    ss << "]\n\n";

    return ss.str();
  }

 private:
  int n_;
  Node* root_;
};

struct Tag {
 public:
  std::array<int, 3> t_;

 public:
  Tag(std::array<int, 3> t = {0, 1, 2}) : t_(t) {}

  bool NeedPropagation() { return t_ != std::array<int, 3>({0, 1, 2}); }

  void Apply(int, int, const Tag& tag) {
    std::array<int, 3> temp = t_;
    for (int i = 0; i < 3; ++i) {
      t_[i] = tag.t_[temp[i]];
    }
  }

  void Reset() { t_ = {0, 1, 2}; }

  std::string to_string() const { return serialize(t_); }
};

struct Data {
 public:
  std::array<i64, 3> a_;
  std::array<std::array<i64, 3>, 3> b_;

 public:
  Data(std::array<i64, 3> a = {0, 0, 0},
       std::array<std::array<i64, 3>, 3> b = {std::array<i64, 3>({0, 0, 0}),
                                              std::array<i64, 3>({0, 0, 0}),
                                              std::array<i64, 3>({0, 0, 0})})
      : a_(a), b_(b) {}

  void Apply(int left, int right, const Tag& tag) {
    int length = right - left + 1;

    std::array<i64, 3> a = a_;
    std::array<std::array<i64, 3>, 3> b = b_;

    a_ = {0, 0, 0};
    for (int i = 0; i < 3; ++i)
      a_[tag.t_[i]] += a[i];

    b_ = {std::array<i64, 3>({0, 0, 0}), std::array<i64, 3>({0, 0, 0}),
          std::array<i64, 3>({0, 0, 0})};
    for (int i = 0; i < 3; ++i)
      for (int j = 0; j < 3; ++j)
        b_[tag.t_[i]][tag.t_[j]] += b[i][j];
  }

  friend Data operator+(const Data& lhs, const Data& rhs) {
    Data result;

    for (int i = 0; i < 3; ++i)
      result.a_[i] = lhs.a_[i] + rhs.a_[i];

    for (int i = 0; i < 3; ++i)
      for (int j = 0; j < 3; ++j)
        result.b_[i][j] = lhs.b_[i][j] + rhs.b_[i][j];

    for (int i = 0; i < 3; ++i)
      for (int j = 0; j < 3; ++j)
        result.b_[i][j] += lhs.a_[i] * rhs.a_[j];

    return result;
  }

  std::string to_string() const { return serialize(a_) + ", " + serialize(b_); }
};

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

  std::vector<Data> a(n);
  for (int i = 0; i < n; ++i) {
    int x;
    std::cin >> x;
    a[i].a_[x]++;
  }

  SegmentTree<Data, Tag> seg(a);
  for (int i = 0; i < m; ++i) {
    int op, l, r;
    std::cin >> op >> l >> r;
    --l, --r;
    logd(op, l, r);

    if (op == 1) {
      const Data d = seg.Query(l, r);
      std::cout << d.b_[1][0] + d.b_[2][1] + d.b_[2][0] << "\n";
      logd(d);
    } else if (op == 2) {
      int x, y, z;
      std::cin >> x >> y >> z;
      seg.Update(l, r, Tag({x, y, z}));
    }
    logd(seg.to_string());
  }
}

Ex - No-capture Lance Game

To be solved.

posted @ 2022-09-06 13:02  _Backl1ght  阅读(154)  评论(0编辑  收藏  举报