AtCoder Beginner Contest 250 解题报告
这里是时隔 \(n\) 个月的解题报告,由于 A 至 C 题过于简单,本解题报告从 D 题开始。
D
Description
若一个正整数 \(k\) 满足 \(k = p \times q^3\)(其中 \(p, q\) 均为质数且 \(p < q\)),则称 \(k\) 和 250 相似。
问对于给定的 \(n\),不超过 \(n\) 的正整数中有多少是和 250 相似。多组数据。
数据范围:\(1 \le n \le 10^{18}\).
Solution
注意到 \(k = p \times q^3\) 中有 \(q^3\),从而不难发现当 \(1 \le n \le 10^{18}\) 时,一个和 250 相似的数中 \(q\) 只能为不超过 \(10^6\) 的质数。
考虑到我们枚举 \(p\) 时只需枚举到 \(\dfrac{n}{q^3}\),所以这个算法应该会跑的很快,但是由于笔者能力不足,不会分析此算法的时间复杂度(实际上是没有仔细想,不过实际可以通过本题)。
Code
Code
/*
* @author Nickel_Angel (1239004072@qq.com)
* @copyright Copyright (c) 2022
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using std::min;
using std::max;
using std::sort;
using std::swap;
using std::vector;
using std::pair;
typedef long long ll;
const int maxn = 1e6 + 10;
ll n;
int prime[maxn], tot;
bool vis[maxn];
inline void Euler(int n)
{
for (int i = 2; i <= n; ++i)
{
if (!vis[i])
prime[++tot] = i;
for (int j = 1; j <= tot && prime[j] <= n / i; ++j)
{
vis[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
}
int main()
{
Euler(1e6);
scanf("%lld", &n);
ll q, ans = 0;
for (int i = 2; i <= tot; ++i)
{
q = 1ll * prime[i] * prime[i] * prime[i];
for (int j = 1; j < i; ++j)
{
if (q * prime[j] > n)
break;
++ans;
}
}
printf("%lld\n", ans);
return 0;
}
E
Description
给定两个长度为 \(n\) 的数组 \(\{a_n\}, \{b_n\}\)。
现在给定 \(q\) 组询问,每组询问形如:由 \(\{a_n\}\) 的前 \(x\) 项组成的集合与 \(\{b_n\}\) 的前 \(y\) 项组成的集合是否相等?如果相等,输出 Yes,否则输出 No.
数据范围:\(1 \le n, q \le 2 \times 10^5, 1 \le a_i, b_i \le 10^9\).
Solution
设 \(firB_x\) 表明 \(x\) 在 \(\{b_n\}\) 中第一次出现的位置,如果 \(x\) 在 \(\{b_n\}\) 中未出现,则为 \(+\infty\),\(firA_x\) 同理。
考虑对于一个特定的 \(x\)。由 \(a_1, a_2, \cdots, a_x\) 组成的集合,设 \(Y = \max_{i = 1}^{x} \limits firB_{a_i}, X = \max_{i = 1}^{y} \limits firA_{b_i}\),则对于询问 \((x, y)\),当且仅当 \(X \le y\) 且 \(Y \le x\)。
故直接考虑求出 \(firA, firB\) 的前缀最大值,就可以做到 \(O(1)\) 回答询问。
Code
/*
* @author Nickel_Angel (1239004072@qq.com)
* @copyright Copyright (c) 2022
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
using std::min;
using std::max;
using std::sort;
using std::swap;
using std::vector;
using std::pair;
typedef long long ll;
const int maxn = 2e5 + 10;
int n, m, a[maxn], b[maxn], ansA[maxn], ansB[maxn];
std::map<int, int> firA, firB;
std::priority_queue<int> q;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", a + i);
if (!firA.count(a[i]))
firA[a[i]] = i;
}
for (int i = 1; i <= n; ++i)
{
scanf("%d", b + i);
if (!firB.count(b[i]))
firB[b[i]] = i;
}
for (int i = 1; i <= n; ++i)
{
if (!firB.count(a[i]))
q.push(n + 1);
else
q.push(firB[a[i]]);
ansA[i] = q.top();
}
while (!q.empty())
q.pop();
for (int i = 1; i <= n; ++i)
{
if (!firA.count(b[i]))
q.push(n + 1);
else
q.push(firA[b[i]]);
ansB[i] = q.top();
}
scanf("%d", &m);
int x, y;
while (m--)
{
scanf("%d%d", &x, &y);
puts(ansA[x] <= y && ansB[y] <= x ? "Yes" : "No");
}
return 0;
}
F
Description
给定由 \(n\) 个整点 \((x_i, y_i)\) 组成的凸多边形,现在要选择两个非相邻的顶点,将多边形按照两个顶点的连线切成两半,然后可以任意选择一半多边形。
现在希望切一刀后选择的一半多边形的面积 \(b\),尽可能接近整个多边形的面积的 \(\dfrac{1}{4}\)(设为 \(a\))。即最小化 \(\left\vert a - b \right\vert\),最终输出 \(8 \times \left\vert a - b \right\vert\),可以证明其恒为整数。
数据范围:\(4 \le n \le 10^5, \left\vert x_i \right\vert, \left\vert y_i \right\vert \le 10^4\).
Solution
由计算几何基础,我们可以得出一个 \(n\) 边形的面积为其各相邻边叉积的和的 \(\dfrac{1}{2}\)。
所以我们可以考虑双指针,即对于一个给定的 \((x_l, y_l)\),考虑由 \(\{(x_l, y_l), (x_{l + 1}, y_{l + 1}), \cdots, (x_r, y_r)\}\) 顶点组成的多边形,我们不断移动 \(r\) 指针,使得这个多边形的面积 \(S\) 刚好不超过 \(a\)。
我们只需考虑计算出所有 \(l\) 的答案后取最小的即可。
Code
/*
* @author Nickel_Angel (1239004072@qq.com)
* @copyright Copyright (c) 2022
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
typedef long long ll;
const int maxn = 2e5 + 10;
int n, x[maxn], y[maxn];
ll s[maxn];
inline ll cross(ll a, ll b, ll c, ll d)
{
return a * d - b * c;
}
inline ll calc(int l, int r)
{
return s[r] - s[l] + cross(x[r], y[r], x[l], y[l]);
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%d%d", x + i, y + i);
x[i + n] = x[i], y[i + n] = y[i];
}
for (int i = 0; i <= 2 * n; ++i)
s[i + 1] = s[i] + cross(x[i], y[i], x[i + 1], y[i + 1]);
ll ans = 7e18;
int r = 0;
for (int l = 0; l < n; ++l)
{
while (calc(l, r + 1) * 4 <= s[n])
++r;
ans = std::min(ans, llabs(s[n] - calc(l, r) * 4));
ans = std::min(ans, llabs(s[n] - calc(l, r + 1) * 4));
}
printf("%lld\n", ans);
return 0;
}
G
Description
已知接下来 \(n\) 天的股票价格 \(p_1, p_2, \cdots, p_n\),每天你要么买进一股股票,要么卖出一股股票,要么什么也不做。\(n\) 天之后你拥有的股票应为 0,当然,希望这 \(n\) 天内能够赚足够多的钱。
数据范围:\(1 \le n \le 2 \times 10^5, 1 \le p_i \le 10^9\).
Solution
本题为原题 CF865D,是一道反悔贪心题目。
我们考虑一个贪心策略,即我们在股票价格低的时候买入股票,在股票价格高的时候卖出股票。但是这样简单的贪心会出现问题,即如果选择在一天卖出股票,可能在后面会遇到股票价格更高的情况,所以我们考虑在贪心中加入反悔机制。
考虑以下反悔机制:我们在第 \(i\) 天买入了一股股票,在第 \(j\) 天卖出,但是我们后面发现第 \(k\) 天卖出更好,我们将其改为第 \(k\) 天卖出,这时我们的收益为 \(p_k - p_i\)。我们发现其相当于在第 \(i\) 天买入一股后在第 \(j\) 天卖出,又在第 \(j\) 天买入一股,在第 \(k\) 天卖出,这个过程中我们的总收益 \(p_j - p_i + p_k - p_j\) 无疑与前面直接改到第 \(k\) 天卖出的收益是相同的。
我们考虑使用小根堆维护前 \(i\) 天我们可以买入一股股票的最小价格 \(p_{min}\)。当 \(p_i > p_{min}\) 时,我们就可以直接将 \(p_i - p_{min}\) 计入答案,然后将 \(p_i\) 加入堆中,表明我们在第 \(i\) 天卖出一股,又为了后面可能的反悔操作,还可以第 \(i\) 天在买入一股。
Code
/*
* @author Nickel_Angel (1239004072@qq.com)
* @copyright Copyright (c) 2022
*/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using std::min;
using std::max;
using std::sort;
using std::swap;
using std::vector;
using std::pair;
typedef long long ll;
const int maxn = 2e5 + 10;
int n, a[maxn];
std::priority_queue<int, vector<int>, std::greater<int>> q;
int main()
{
scanf("%d", &n);
ll ans = 0;
for (int i = 1; i <= n; ++i)
{
scanf("%d", a + i);
if (!q.empty() && q.top() < a[i])
{
ans += a[i] - q.top();
q.pop();
q.push(a[i]);
}
q.push(a[i]);
}
printf("%lld\n", ans);
return 0;
}
Ex
Description
给定一个 \(n\) 个点 \(m\) 条边的无向图,其中经过结点 \(a_i\) 到结点 \(b_i\) 的边需要时间 \(c_i\)。称标号为 \(1, 2, \cdots, k\) 的点为关键点,现在给出 \(q\) 组询问,每组询问形如高桥君想要从 \(x\) 号点走到 \(y\) 号点(保证 \(x, y\) 均为关键点),如果他在上次休息之后持续走了超过 \(t\) 个单位时间,那么他就不能再移动,他在路程中只可以在关键点休息,问是否存在一种路线,使得高桥君可以成功到达 \(y\) 号点。
数据范围:\(2 \le k \le n \le 2 \times 10^5, n - 1 \le m \le 2 \times 10^5, 1 \le c_i \le 10^9, 1 \le q \le 2 \times 10^5, 1 \le t \le 10^{15}\),保证 \(q\) 组询问的 \(t\) 是不减的。
Solution
(似乎写了一个伪证,待验证)