Panasonic Programming Contest 2022(AtCoder Beginner Contest 251)

A,B,C跳过。

D - At Most 3 (Contestant ver.)

题意

构造一个集合:

  • 大小不超过\(300\)的集合
  • 集合中元素大小不能超过\(10^6\)
  • 使得所有\([1,W]\)中的数都可以通过任选集合中不超过\(3\)个元素然后加起来得到。

其中\(W \le 10^6\)

思路

可以无脑输出\(W = 10^6\)的答案,然后这个答案可以按100进制拆分构造。

AC代码
// Problem: D - At Most 3 (Contestant ver.)
// Contest: AtCoder - Panasonic Programming Contest 2022(AtCoder Beginner
// Contest 251) URL: https://atcoder.jp/contests/abc251/tasks/abc251_d 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);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  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 w;
  std::cin >> w;

  std::set<int> s;
  for (int i = 1; i <= 99; ++i) {
    s.insert(i);
  }
  for (int i = 1; i <= 99; ++i) {
    s.insert(i * 100);
  }
  for (int i = 1; i <= 100; ++i) {
    s.insert(i * 10000);
  }

  std::cout << s.size() << "\n";
  for (int v : s)
    std::cout << v << " ";
  std::cout << "\n";
}

E - Takahashi and Animals

题意

给定一个长度为\(n\)的数组\(a\)\(a_i\)表示可以花费\(a_i\)的代价选\(i\)\(i+1\),特别的\(a_n\)对应\(n\)\(1\)

问选取\([1,n]\)所有元素的最小代价。

其中\(n \le 3 \times 10^5\)

思路

环不好DP但是链好DP。枚举第一个元素选与不选,就能将环的DP转化成链的DP。

链的DP就是相邻两个元素至少要选一个,挺经典的。

AC代码
// Problem: E - Tahakashi and Animals
// Contest: AtCoder - Panasonic Programming Contest 2022(AtCoder Beginner
// Contest 251) URL: https://atcoder.jp/contests/abc251/tasks/abc251_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);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  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;

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

  if (n == 2) {
    std::cout << std::min(a[1], a[2]) << "\n";
    return;
  }

  i64 ans0, ans1;

  // do not choose a_1
  {
    std::vector<std::vector<i64>> dp(n + 1, std::vector<i64>(2, INT64_MAX));
    dp[2][0] = a[2];
    dp[2][1] = a[2];
    for (int i = 3; i <= n; ++i) {
      dp[i][0] = dp[i - 1][1];
      dp[i][1] = std::min(dp[i - 1][0], dp[i - 1][1]) + a[i];
    }
    ans0 = dp[n][1];
  }

  // choose a_1
  {
    std::vector<std::vector<i64>> dp(n + 1, std::vector<i64>(2, INT64_MAX));
    dp[3][0] = a[2];
    dp[3][1] = a[3];
    for (int i = 4; i <= n - 1; ++i) {
      dp[i][0] = dp[i - 1][1];
      dp[i][1] = std::min(dp[i - 1][0], dp[i - 1][1]) + a[i];
    }
    dp[n - 1][0] = dp[n - 2][1] + a[n];
    ans1 = std::min(dp[n - 1][0], dp[n - 1][1]) + a[1];
  }

  std::cout << std::min(ans0, ans1) << "\n";
}

F - Two Spanning Trees

题意

给一个无向图,让你构造两棵生成树:

  1. 所有非树边\((u, v)\)\(u\)\(v\)其中一个是另外一个的祖先成立。
  2. 所有非树边\((u, v)\)\(u\)\(v\)其中一个是另外一个的祖先不成立。

图中点数和边数至多为\(2 \times 10^5\)

思路

第一种是DFS树,第二种是BFS树。

AC代码
// Problem: F - Two Spanning Trees
// Contest: AtCoder - Panasonic Programming Contest 2022(AtCoder Beginner
// Contest 251) URL: https://atcoder.jp/contests/abc251/tasks/abc251_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);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

void work1(const std::vector<std::vector<int>>& g) {
  int n = g.size();
  std::vector<bool> vis(n, false);
  std::vector<std::pair<int, int>> a;
  std::function<void(int, int)> dfs = [&](int u, int fa) {
    if (fa != -1)
      a.push_back(std::make_pair(fa, u));
    vis[u] = true;
    for (int v : g[u]) {
      if (v == fa || vis[v])
        continue;
      dfs(v, u);
    }
  };
  dfs(0, -1);
  for (auto [u, v] : a)
    std::cout << u + 1 << " " << v + 1 << "\n";
}

void work2(const std::vector<std::vector<int>>& g) {
  int n = g.size();
  std::vector<bool> vis(n, false);
  std::vector<std::pair<int, int>> a;

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

    for (int v : g[u]) {
      if (!vis[v]) {
        vis[v] = true;
        q.push(v);
        a.push_back(std::make_pair(u, v));
      }
    }
  }
  for (auto [u, v] : a)
    std::cout << u + 1 << " " << v + 1 << "\n";
}

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

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

G - Intersection of Polygons

题意

给定一个有\(n\)个点的凸包\(P\)

给定\(m\)个平移\((x_i, y_i)\),表示凸包\(P_i\)是通过将\(P\)沿着\((x_i, y_i)\)平移得到。

给定\(q\)个询问\((x_i, y_i)\),问是不是\(m\)个平移生成的凸包\(P_i\)都包含点\((x_i, y_i)\)

其中\(n \le 50, m, q \le 2 \times 10^5\)

思路

将凸包转换成半平面交,求出\(n \times m\)个半平面的交,如果点位于交内,则Yes,否则No

这里由于凸包是平移生成的,所以可以将一条边平移生成的多个半平面归到一类,每一类保留最严格的那个半平面即可快速求出半平面交。

AC代码
// Problem: G - Intersection of Polygons
// Contest: AtCoder - Panasonic Programming Contest 2022(AtCoder Beginner
// Contest 251) URL: https://atcoder.jp/contests/abc251/tasks/abc251_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);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

int sgn(i64 x) {
  if (x < 0)
    return -1;
  if (x > 0)
    return 1;
  return 0;
}

struct point {
  int x, y;
  point() {}
  point(int _x, int _y) : x(_x), y(_y) {}

  friend point operator+(const point& a, const point& b) {
    return point(a.x + b.x, a.y + b.y);
  }

  friend point operator-(const point& a, const point& b) {
    return point(a.x - b.x, a.y - b.y);
  }
};

i64 det(const point& a, const point& b) {
  return i64(1) * a.x * b.y - i64(1) * a.y * b.x;
}

struct line {
  point s, e;
  line() {}
  line(point _s, point _e) : s(_s), e(_e) {}

  int relationToPoint(const point& a) {
    i64 area = det(e - s, a - s);
    return sgn(area);
  }
};

void solve_case(int Case) {
  int n;
  std::cin >> n;
  std::vector<point> p(n);
  for (int i = 0; i < n; ++i)
    std::cin >> p[i].x >> p[i].y;

  std::vector<line> h(n);
  int m;
  std::cin >> m;
  for (int _ = 0; _ < m; ++_) {
    point d;
    std::cin >> d.x >> d.y;

    for (int i = 0; i < n; ++i) {
      point s = p[i] + d;
      point e = p[(i + 1) % n] + d;
      if (_ == 0 || h[i].relationToPoint(s) != -1) {
        h[i] = line(s, e);
      }
    }
  }

  int q;
  std::cin >> q;
  for (int _ = 0; _ < q; ++_) {
    point a;
    std::cin >> a.x >> a.y;

    bool flag = true;
    for (int i = 0; i < n; ++i) {
      if (h[i].relationToPoint(a) == -1) {
        flag = false;
      }
    }

    std::cout << (flag ? "Yes" : "No") << "\n";
  }
}

Ex - Fill Triangle

题意

给你定一个数字三角形的第\(n\)层,让你求出第\(k\)层。

记第\(i\)层第\(j\)个元素为\(B_{i, j}\),则\(B_{i, j} = (B_{i + 1, j} + B_{i + 1, j + 1}) \mod 7\)

\(n \le 10^9, k \le \min(n, 10^5)\)且第\(n\)层中的数字可以分解成不超过\(200\)个连续且序列中元素值都相等的极大子序列。

思路

\(\binom{i}{j} = (\binom{i + 7^n}{j} + \binom{i + 7^n}{j + 7^n}) \mod 7\)

根据这个就可以选最大的\(d\)满足\(n - 7^d \ge k\),然后算出\(n - 7^d\)行。不断往上跳就能计算出第\(k\)行的情况。

每次跳的时候就双指针模拟一下。

AC代码
// Problem: Ex - Fill Triangle
// Contest: AtCoder - Panasonic Programming Contest 2022(AtCoder Beginner
// Contest 251) URL: https://atcoder.jp/contests/abc251/tasks/abc251_h Memory
// Limit: 1024 MB Time Limit: 4000 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);
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  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, m, k;
  std::cin >> n >> m >> k;

  std::vector<std::pair<int, int>> a(m);
  for (int i = 0; i < m; ++i)
    std::cin >> a[i].first >> a[i].second;

  while (n > k) {
    int d = 1;
    while (n - 7 * d >= k)
      d = d * 7;

    std::vector<std::pair<int, int>> b;
    int la = 0, ra = 0;
    int tempd = d;
    while (tempd > a[ra].second) {
      tempd -= a[ra].second;
      ++ra;
    }
    int cl = a[la].second, cr = a[ra].second - tempd;

    while (ra < a.size()) {
      int first = (a[la].first + a[ra].first) % 7;
      int second = std::min(cl, cr);

      if (b.empty() || b.back().first != first) {
        b.emplace_back(first, second);
      } else {
        b.back().second += second;
      }

      cl -= second, cr -= second;
      if (cl == 0)
        cl = a[++la].second;
      if (cr == 0)
        cr = a[++ra].second;
    }

    a = b;
    n -= d;
    logd(d, k);
  }

  for (auto [f, s] : a) {
    for (int i = 0; i < s; ++i)
      std::cout << f << " ";
  }
  std::cout << "\n";
}
posted @ 2022-05-15 15:31  _Backl1ght  阅读(121)  评论(0编辑  收藏  举报