AtCoder Beginner Contest 262

E - Red and Blue Graph

题意

给一个\(n\)个点\(m\)条边的无向图,现在要将每个点染成红色或者蓝色,问满足以下两个条件的染色方案数:

  1. 恰好有\(k\)个点是红色。
  2. 两端颜色不同的边数为偶数。

其中\(1 \le n \le 2 \times {10}^5\)

思路

假设红色点的度之和为\(a\),两端都为红色的边数为\(b\),两端异色的边数为\(c\),则有\(a = 2b + c\)

由此\(c\)为偶数当且仅当\(a\)为偶数。

现在问题转化成选\(k\)个点使得所选点度数之和为偶数的方案数,这个就是简单组合数学了,枚举选多少个度数为奇数的点,然后累加即可。

AC代码
// Problem: E - Red and Blue Graph
// Contest: AtCoder - AtCoder Beginner Contest 262
// URL: https://atcoder.jp/contests/abc262/tasks/abc262_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];
  }
} binom(2e5 + 5);

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

  std::vector<int> deg(n);
  for (int i = 0; i < m; ++i) {
    int u, v;
    std::cin >> u >> v;
    --u, --v;
    ++deg[u], ++deg[v];
  }

  int c[2] = {0, 0};
  for (int i = 0; i < n; ++i)
    ++c[deg[i] % 2];

  Mint ans(0);
  for (int i = 0; i < n; i += 2) {
    ans = ans + binom(c[0], k - i) * binom(c[1], i);
  }

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

F - Erase and Rotate

题意

给定一个长度为\(n\)的排列\(p\),支持以下两种操作:

  1. 删除一个元素。
  2. 将最后一个元素移动到开头。

问通过不超过\(k\)次操作,能得到的字典序最小的结果。

其中\(1 \le n \le 2 \times {10}^5\)

思路

问就是贪。

两个策略:不使用操作2,和至少使用1次操作2。

第一种策略:如果一个元素大于下一个元素,就应该把它删掉。然后字典序前面权重较大,从前往后模拟一下就可以了。

第二种策略:还是贪,假设后\(k\)个元素中的最小值下标为\(x\),那么这种策略下把\(x\)搞到开头最优。然后再类似第一种策略模拟一下。

如果有剩余的操作次数,还可以不断将尾部删去。

AC代码
// Problem: F - Erase and Rotate
// Contest: AtCoder - AtCoder Beginner Contest 262
// URL: https://atcoder.jp/contests/abc262/tasks/abc262_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) {
  auto work0 = [](std::vector<int> p, int k) {
    if (k == 0)
      return p;

    int n = p.size();

    std::vector<int> r;
    for (int i = 0; i < n; ++i) {
      while (!r.empty() && r.back() > p[i] && k > 0) {
        --k;
        r.pop_back();
      }
      r.push_back(p[i]);
    }

    while (k > 0) {
      --k;
      r.pop_back();
    }

    return r;
  };

  auto work1 = [](std::vector<int> p, int k) {
    if (k == 0)
      return p;

    int n = p.size();

    int x = n - 1;
    for (int i = n - k; i < n; ++i) {
      if (p[i] < p[x]) {
        x = i;
      }
    }

    k -= (n - x);
    std::vector<int> r;
    std::vector<int> c(n, 1);
    for (int i = x; i < n; ++i) {
      c[p[i]] = 0;
      while (!r.empty() && r.back() > p[i]) {
        r.pop_back();
      }
      r.push_back(p[i]);
    }
    for (int i = 0; i < x; ++i) {
      while (!r.empty() && r.back() > p[i] && k >= c[r.back()]) {
        k -= c[r.back()];
        r.pop_back();
      }
      r.push_back(p[i]);
    }

    while (k >= c[r.back()]) {
      k -= c[r.back()];
      r.pop_back();
    }

    return r;
  };

  int n, k;
  std::cin >> n >> k;

  std::vector<int> p(n);
  for (int i = 0; i < n; ++i) {
    std::cin >> p[i];
    --p[i];
  }

  auto ans = std::min(work0(p, k), work1(p, k));
  for (int i = 0; i < static_cast<int>(ans.size()); ++i)
    std::cout << ans[i] + 1 << " \n"[i + 1 == static_cast<int>(ans.size())];
}

G - LIS with Stack

题意

给定一个长度为\(n\)的序列\(a\),从前往后枚举元素,对于每一个元素,你可以:

  1. 将其加入栈\(S\)
  2. 将其加到序列\(X\)的尾部。

在任意时刻,你可以将栈\(S\)的顶部出栈并加到序列\(X\)的尾部。

\(X\)满足非降序时,得分为\(|X|\),反之得分为\(0\)

问可能的最大分数。

其中\(1\le n, a_i \le 50\)

思路

DP。

\(dp(l, r, mn, mx)\)表示仅用\(a_{l}, a_{l + 1}, \dots, a_{r}\)构造,且满足\(\forall v \in X, mn \le v \le mx\),的最大\(|X|\)

\(S(l, r, mn, mx)\)表示某个满足上述条件的\(X\)

\(X\)可能不包含\(a_l\),所以\(dp(l + 1, r, mn, mx)\)可以用来更新\(dp(l, r, mn, mx)\)

\(X\)可能包含\(a_l\),然后由于栈的存在又可以细分为以下几种情况:

  1. \(a_l\),然后跟接\(S(l + 1, r, a_l, mx)\)
  2. \(S(l + 1, r, mn, a_l)\),后面接\(a_l\)
  3. \(S(l + 1, m, mn, a_l)\)\(a_l\)再接\(S(m + 1, r, a_l, mx)\)
AC代码
// Problem: G - LIS with Stack
// Contest: AtCoder - AtCoder Beginner Contest 262
// URL: https://atcoder.jp/contests/abc262/tasks/abc262_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 N = 55;
int dp[N][N][N][N];

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

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

  memset(dp, -1, sizeof(dp));
  std::function<int(int, int, int, int)> DP = [&](int l, int r, int mn, int mx) {
    if (l > r)
      return 0;
    if (l == r)
      return (a[l] >= mn && a[l] <= mx) ? 1 : 0;

    if (dp[l][r][mn][mx] == -1) {
      int result = 0;

      // 0
      result = std::max(result, DP(l + 1, r, mn, mx));

      // 1
      if (a[l] >= mn && a[l] <= mx) {
        for (int m = l; m <= r; ++m) {
          result = std::max(result, DP(l + 1, m, mn, a[l]) + 1 + DP(m + 1, r, a[l], mx));
        }
      }

      dp[l][r][mn][mx] = result;
    }

    return dp[l][r][mn][mx];
  };

  std::cout << DP(0, n - 1, 1, 50) << "\n";
}

Ex - Max Limited Sequence

To be solved.

posted @ 2022-07-31 23:01  _Backl1ght  阅读(155)  评论(1编辑  收藏  举报