VP Educational Codeforces Round 58 (Rated for Div. 2)
A. Minimum Integer
题意:找最小的小于\(l\)或者大于\(r\)的\(d\)的倍数。
点击查看代码
void solve() {
int l, r, d;
std::cin >> l >> r >> d;
if (l > d) {
std::cout << d << "\n";
} else {
std::cout << (r / d + 1) * d << "\n";
}
}
B. Accordion
题意:合法字符串为"\([:\)" + 若干个"\(|\)" + "\(:]\)"。你可以删除当前字符串的一些字符,可以得到的最长合法字符串的长度是多少?
找出最左边的"\([:\)和最右边的"\(:]\)"。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
int l = 0, j = 0;
while (l < n && j < 2) {
if (j == 0 && s[l] == '[') {
++ j;
} else if (j == 1 && s[l] == ':') {
++ j;
}
++ l;
}
int r = n - 1;
j = 0;
while (r >= 0 && j < 2) {
if (j == 0 && s[r] == ']') {
++ j;
} else if (j == 1 && s[r] == ':') {
++ j;
}
-- r;
}
if (l > r + 1) {
std::cout << -1 << "\n";
return;
}
int ans = 4;
for (int i = l; i <= r; ++ i) {
ans += s[i] == '|';
}
std::cout << ans << "\n";
}
C. Division and Union
题意:\(n\)个区间,分成两个非空组。使得每组任意一个区间和另一个的任意一个区间都不相交。
双指针典题。实际是求区间合并后的区间数是不是大于1。
你们可以按左端点排序,然后维护一个右端点的最大值。每次遇到一个新点,如果它的左端点小于等于最大值,那么它和之前区间有交。否则没交,我们就可以分成两个组了。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::array<int, 3>> a(n);
for (int i = 0; i < n; ++ i) {
int l, r;
std::cin >> l >> r;
a[i] = {l, r, i};
}
std::ranges::sort(a);
std::vector<int> ans(n);
for (int i = 0, j = 0, max = a[i][1]; i < n; ++ i) {
if (a[i][0] > max) {
break;
}
while (j < n && a[j][0] <= max) {
max = std::max(max, a[j][1]);
ans[a[j][2]] = 1;
++ j;
}
if (j == n) {
std::cout << -1 << "\n";
return;
}
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] + 1 << " \n"[i == n - 1];
}
}
D. GCD Counting
题意:给你一棵树,记\(g(x, y)\)为\(x\)到\(y\)的路径的\(gcd\),求所有\(g(x, y) > 1\)的\(x, y\)的最大\(dist(x, y)\)。
考虑树形\(dp\)。将每个数进行质因子分解。然后\(f[i][j]\)记为以\(i\)为根的子树包含\(i\)且最大公约数是\(a_i\)的第\(j\)个质因子的倍数的路径的最大长度。那么就类似于求树的直径,我们可以枚举所有\(u, v\)相同的质因子,\(ans = \max(ans, f[u][i] + f[v][j])\),\(f[u][i] = \max(f[u][i], f[v][j] + 1)\)。其中\(ans\)是路径最大值。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
if (std::ranges::count(a, 1) == n) {
std::cout << 0 << "\n";
return;
}
const int N = 2e5 + 5;
std::vector<std::vector<int>> g(N);
for (int i = 2; i < N; ++ i) {
int x = i;
for (int j = 2; j <= x / j; ++ j) {
if (x % j == 0) {
g[i].push_back(j);
while (x % j == 0) {
x /= j;
}
}
}
if (x > 1) {
g[i].push_back(x);
}
}
std::vector f(n, std::vector<int>(10, 1));
int ans = 1;
auto dfs = [&](auto & self, int u, int fa) -> void {
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
self(self, v, u);
for (int i = 0; i < g[a[u]].size(); ++ i) {
for (int j = 0; j < g[a[v]].size(); ++ j) {
if (g[a[u]][i] == g[a[v]][j]) {
ans = std::max(ans, f[u][i] + f[v][j]);
f[u][i] = std::max(f[u][i], f[v][j] + 1);
}
}
}
}
};
dfs(dfs, 0, -1);
std::cout << ans << "\n";
}
E. Polycarp's New Job
题意:两种操作,一种是把\((x, y)\)加入集合,一种是求集合里每个数是不是都有\(x \leq\ h\ and\ y \leq w\)或\(x \leq\ w\ and\ y \leq\ h\)。
对于\(h \geq w\),且\(x \geq y\),如果\((y, x)\)可以满足,则\((x, y)\)也能满足,所以我们让第一关键字大于等于第二关键字存下来就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
int x = 0, y = 0;
for (int i = 0; i < n; ++ i) {
std::string op;
std::cin >> op;
if (op == "+") {
int a, b;
std::cin >> a >> b;
if (a < b) {
std::swap(a, b);
}
x = std::max(x, a), y = std::max(y, b);
} else {
int h, w;
std::cin >> h >> w;
if (h < w) {
std::swap(h, w);
}
if (x <= h && y <= w) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
}
}
F. Trucks and Cities
题意:数轴上有\(n\)个点,有\(m\)辆车,第\(i\)辆车从\(s_i\)到\(t_i\),一格花费\(c_i\)升油,中途可以最多在\(r_i\)个点加油。假设油箱容量为\(V\),则加满油后容量变成\(V\),一开始油箱是满的。每辆车的油箱容量是一样的,求最小的\(V\)使得所有车可以走完。
观察到\(n\)比较小,可以使用区间\(dp\)。\(f[i][j][k]\)表示从\(i\)到\(j\)加油\(k\)次的路径段的最大段最小的长度。你们一辆花费为\(c_i\)的车油箱容量为\(f[i][j][k] \times c_i\)。
转移就是枚举前\(k-1\)个最后加油的地点,\(f[i][j][k] = \min(f[i][l][k - 1], a[j] - a[l])\)。
只不过这个复杂度是\(n^4\)。无法通过。考虑优化,发现\(l\)从\(i+1\)开始,随着\(l\)增大,\(f[i][l][k-1]\)递增,\(a[j] - a[l]\)递减。因为是求两者最大值。所以答案一定在最后一个\(f[i][l][k-1] < a[j] - a[l]\)上,用个指针维护就行。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<std::array<int, 3>>> Q(n);
for (int i = 0; i < m; ++ i) {
int s, t, c, r;
std::cin >> s >> t >> c >> r;
-- s, -- t;
Q[std::min(r, t - s)].push_back(std::array<int, 3>{s, t, c});
}
const int inf = 1e9;
std::vector f(n, std::vector<int>(n, inf));
for (int i = 0; i < n; ++ i) {
for (int j = i; j < n; ++ j) {
f[i][j] = a[j] - a[i];
}
}
i64 ans = 0;
for (int k = 0; k < n; ++ k) {
for (auto & [s, t, c] : Q[k]) {
ans = std::max(ans, (i64)f[s][t] * c);
}
auto g = f;
for (int i = 0, l = i; i < n; ++ i) {
for (int j = i + 1; j < n; ++ j) {
while (f[i][l] < a[j] - a[l]) {
++ l;
}
g[i][j] = std::min({g[i][j], f[i][l], a[j] - a[l - 1]});
}
}
f = g;
}
std::cout << ans << "\n";
}