VP Educational Codeforces Round 7
A. Infinite Sequence
题意:一个数列是由\(\{\{1\}, \{1, 2\}, ... \{1, 2, ..., n\}\}\)组成的,求第\(k\)个位置上的数。
发现前\(n\)段数组成序列长度为\(\frac{n\times (n + 1)}{2}\),那么二分找到这个\(n\),\(k\)减去\(\frac{n\times (n + 1)}{2}\)就是答案。
点击查看代码
void solve() {
i64 n;
std::cin >> n;
i64 l = 0, r = 1e9;
while (l < r) {
i64 mid = l + r + 1 >> 1ll;
if (mid * (mid + 1) / 2 < n) {
l = mid;
} else {
r = mid - 1;
}
}
n -= l * (l + 1) / 2;
std::cout << n << "\n";
}
B. The Time
题意:给你一个时间,求\(a\)分钟后的时间。
模拟进位即可。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int a = 0;
std::cin >> a;
a %= 60 * 24;
int b[] = {10, 10, 0, 6, 10};
for (int i = 4; i >= 0; -- i) {
if (s[i] == ':') {
continue;
}
int x = s[i] - '0' + a;
s[i] = '0' + (x % b[i]);
a = x / b[i];
}
int x = (s[0] - '0') * 10 + s[1] - '0';
x %= 24;
s[0] = x / 10 + '0';
s[1] = x % 10 + '0';
std::cout << s << "\n";
}
C. Not Equal on a Segment
题意:给你一个数组,每次求\(l, r\)区间内不是\(x\)的一个数的位置。
把每个数出现的位置存下来。然后二分出大于等于\(l\)和大于等于\(r\)的两个边界,特判边界是不是和\(l, r\)相等。如果相等,则可以二分找到第一个位置差不等于出现位置差的位置。
点击查看代码
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];
}
const int N = 1e6 + 5;
std::vector<std::vector<int>> pos(N);
for (int i = 0; i < n; ++ i) {
pos[a[i]].push_back(i);
}
while (m -- ) {
int l, r, x;
std::cin >> l >> r >> x;
-- l, -- r;
int pl = std::lower_bound(pos[x].begin(), pos[x].end(), l) - pos[x].begin();
int pr = std::lower_bound(pos[x].begin(), pos[x].end(), r) - pos[x].begin();
if (pl == pos[x].size() || pos[x][pl] != l) {
std::cout << l + 1 << "\n";
continue;
}
if (pr == pos[x].size() || pos[x][pr] != r) {
std::cout << r + 1 << "\n";
continue;
}
int L = pl, R = pr;
while (L < R) {
int mid = L + R >> 1;
if (pos[x][mid] - l != mid - pl) {
R = mid;
} else {
L = mid + 1;
}
}
if (pos[x][L] - l == L - pl) {
std::cout << -1 << "\n";
} else {
std::cout << pos[x][L] << "\n";
}
}
}
D. Optimal Number Permutation
题意:有一个\(2n\)长的数组,\([1, n]\)每个数恰好出现两次。假设\(i\)出现的两个位置为\(x_i, y_i(x_i < y_i)\),定义\(i\)的价值为为\((n - i) \times (y_i - x_i + i - n)\)。要让\(\sum_{i = 1}^{n} (n - i) \times (y_i - x_i + i - n)\)最小。
发现乘式前面是固定的,考虑怎么操作后面,\(i - n\)显然是个负数,如果能让\(y_i - x_i = - (i - n)\)岂不是可以每个数的值为零?考虑怎么构造,那么\(y_i = x_i + n - i\),发现我们可以用前\(n\)个位置把除\(n\)外的所有奇数构造出来,就是两边开始放最小值就行了。同理,后面\(n\)个数也可以满足除\(n\)外的所有奇数。最后两个\(n\)放两个空位就行了,因为\(n\)的值一定是0,怎么放无所谓。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> ans(n * 2);
for (int i = 0, x = 1; x < n; ++ i, x += 2) {
ans[i] = ans[i + n - x] = x;
}
for (int i = (n / 2 + n % 2) * 2, x = 2; x < n; ++ i, x += 2) {
ans[i] = ans[i + n - x] = x;
}
for (int i = 0; i < 2 * n; ++ i) {
if (ans[i] == 0) {
ans[i] = n;
}
}
for (int i = 0; i < 2 * n; ++ i) {
std::cout << ans[i] << " \n"[i == 2 * n - 1];
}
}
E. Ants in Leaves
题意:给你一棵树,每个叶子上有一只蚂蚁,除根节点外,其它节点一个时刻只能由一直蚂蚁,求所有蚂蚁到根节点的最短时间。
因为根节点没有限制,所以可以讨论根节点的每个子树,对于每棵子树,如果只有一个叶子,那么答案就是\(d_i\),其中\(d\)是叶子的深度,如果有两个,那么就是两个\(d\)中较大的那一个,但如果两个深度相同,则有一个需要等一秒,同理,如果有多个相同的,则它们都需要等一秒,然后下一秒又只有一个能上去,剩下的又要等一秒,于是我们把每个子树的叶子深度存下来排序,处理出来最大时间就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
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);
}
std::vector<int> t;
std::vector<int> d(n);
auto dfs = [&](auto self, int u, int fa) -> void {
bool leap = true;
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
d[v] = d[u] + 1;
leap = false;
self(self, v, u);
}
if (leap) {
t.push_back(d[u]);
}
};
int ans = 0;
for (auto & u : adj[0]) {
t.clear();
d[u] = 1;
dfs(dfs, u, 0);
std::sort(t.begin(), t.end());
for (int i = 1; i < t.size(); ++ i) {
t[i] = std::max(t[i], t[i - 1] + 1);
}
ans = std::max(ans, t.back());
}
std::cout << ans << "\n";
}