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
題意
給一個無向圖,讓你構造兩棵生成樹:
- 所有非樹邊\((u, v)\),\(u\)是\(v\)其中一個是另外一個的祖先成立。
- 所有非樹邊\((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";
}