VP Educational Codeforces Round 66 (Rated for Div. 2)
A. From Hero to Zero
题意:你要把\(n\)变成零,每次有两种操作:一种是把\(n\)减一,一种是如果\(k\)整除\(n\)则把\(n\)除以\(k\)。求最小操作数。
能除就除,不能除就减到最近的\(k\)的倍数。
点击查看代码
void solve() {
i64 n, k;
std::cin >> n >> k;
i64 ans = 0;
while (n > 0) {
while (n % k == 0) {
++ ans;
n /= k;
}
ans += n - n / k * k;
n = n / k * k;
}
std::cout << ans << "\n";
}
B. Catch Overflow!
题意:一个编程语言只有三种语句:add, for x, end,要求你输出这些语句的结果。
用栈存每个\(for\)循环自己这一层的\(add\)个数和与外层的乘积。注意溢出。把乘积和\(2^{31}\)次方取最小值。
点击查看代码
void solve() {
int n;
std::cin >> n;
i64 ans = 0;
i64 max = (1ll << 32) - 1;
std::stack<std::pair<i64, i64>> stk;
for (int i = 0; i < n; ++ i) {
std::string s;
std::cin >> s;
if (s == "add" && stk.empty()) {
ans += 1;
if (ans > max) {
std::cout << "OVERFLOW!!!\n";
return;
}
} else {
if (s == "for") {
i64 x;
std::cin >> x;
if (stk.empty()) {
stk.emplace(0, x);
} else {
stk.emplace(0, std::min(max + 1, x * stk.top().second));
}
} else if (s == "add") {
stk.top().first += 1;
} else {
auto [x, y] = stk.top();
if (x * y > max || ans + x * y > max) {
std::cout << "OVERFLOW!!!\n";
return;
}
ans += x * y;
if (ans > max) {
std::cout << "OVERFLOW!!!\n";
return;
}
stk.pop();
}
}
}
std::cout << ans << "\n";
}
C. Electrification
题意:给你\(n\)个点,求一个点使得这\(n\)个点距离它\(k+1\)远的点的距离最小。
这个点距离\(k+1\)小的这些点在序列里一定是连续的。于是我们可以枚举每个\(k+1\)的子数组。要让距离最小,显然取中位数。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
++ k;
int d = 2e9, ans = 0;
for (int i = 0; i + k - 1 < n; ++ i) {
int x = a[i] + a[i + k - 1] >> 1;
if (d > std::max(x - a[i], a[i + k - 1] - x)) {
d = std::max(x - a[i], a[i + k - 1] - x);
ans = x;
}
}
std::cout << ans << "\n";
}
D. Array Splitting
题意:把数组分成\(k\)段,如果\(a_i\)在第\(j\)段,贡献为\(a_i \times j\)。求最大贡献和。
考虑把\(a_i \times j\)拆为\(j\)个\(a_i \times 1\)。那么如果以\(i\)这个位置新开一段,则\([i, n]\)这个后缀的数字都要加一次。于是我们必选\([1, n]\)这个后缀,然后选\(k-1\)个最大的后缀就行。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<i64> b;
i64 suf = 0;
for (int i = n - 1; i > 0; -- i) {
suf += a[i];
b.push_back(suf);
}
i64 ans = suf + a[0];
std::ranges::sort(b, std::greater<>());
for (int i = 0; i + 1 < k; ++ i) {
ans += b[i];
}
std::cout << ans << "\n";
}
E. Minimal Segment Cover
题意:给你\(n\)个区间,对于每个询问的\([l, r]\),求至少需要从中选几个区间才能覆盖它。
考虑倍增,\(f[i][j]\)表从小于等于\(i\)的位置选\(2^j\)个区间能覆盖到的最远的右端点。
那么就做完了。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
const int N = 5e5;
std::vector f(N + 1, std::vector<int>(20));
for (int i = 0; i < n; ++ i) {
int l, r;
std::cin >> l >> r;
f[l][0] = std::max(f[l][0], r);
}
for (int i = 1; i <= N; ++ i) {
f[i][0] = std::max(f[i][0], f[i - 1][0]);
}
for (int j = 1; j < 20; ++ j) {
for (int i = 0; i <= N; ++ i) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
while (m -- ) {
int x, y;
std::cin >> x >> y;
int ans = 0, p = x;
for (int i = 19; i >= 0; -- i) {
if (f[p][i] < y) {
ans += 1 << i;
p = f[p][i];
}
}
if (f[p][0] < y) {
std::cout << -1 << "\n";
} else {
std::cout << ans + 1 << "\n";
}
}
}
F. The Number of Subpermutations
题意:求一个数字的子数组是一个排列的数量。
一个长度为\(n\)的数组是排列,那么需要两个条件:
- 最大值为\(n\)。
- 每个数都只出现一次。
那么对于一个区间\([l, r]\),我们以一个最大值的位置作为\(mid\),把区间分为\([l, mid - 1]\)和\([mid + 1, r]\)两部分,然后我们再计算当前区间跨过\(mid\)的区间的贡献。我们用\(last_i\)记录\(a_i\)前一次出现的位置,\(pre_i = \max_{j=1}^{i} last_j\)。那么对于一个区间\([l, r]\),如果\(pre_r < l\),则这个区间的数都只出现一次,因为每个数的前一次出现的位置都小于\(l\)。那么就满足了作为排列的两个条件。
我们选择左区间和右区间的长度小的那个来枚举端点,时间复杂度是\(O(nlogn)\)。
点击查看代码
template <class Info>
struct ST {
std::vector<std::vector<Info>> st;
ST(std::vector<Info> a) {
int n = a.size(), m = std::__lg(n) + 1;
st.assign(n, std::vector<Info>(m));
for (int i = 0; i < n; ++ i) {
st[i][0] = a[i];
}
for (int j = 1; j < m; ++ j) {
for (int i = 0; i + (1 << j - 1) < n; ++ i) {
st[i][j] = st[i][j - 1] + st[i + (1 << j - 1)][j - 1];
}
}
}
Info query(int l, int r) {
int lg = std::__lg(r - l + 1);
return st[l][lg] + st[r - (1 << lg) + 1][lg];
}
};
struct Info {
int max, p;
};
Info operator + (const Info & a, const Info & b) {
return a.max >= b.max ? a : b;
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
-- a[i];
}
std::vector<Info> info(n);
for (int i = 0; i < n; ++ i) {
info[i] = {a[i], i};
}
std::vector<int> last(n, -1), pre(n, -1);
for (int i = 0; i < n; ++ i) {
if (i > 0) {
pre[i] = std::max(pre[i - 1], last[a[i]]);
}
last[a[i]] = i;
}
ST<Info> st(info);
auto dfs = [&](auto & self, int l, int r) -> i64 {
if (l > r) {
return 0;
}
int mid = st.query(l, r).p;
i64 res = self(self, l, mid - 1) + self(self, mid + 1, r);
int len = a[mid] + 1;
if (mid - l < r - mid) {
for (int i = std::max(l, mid - len + 1); i <= mid && i + len - 1 <= r; ++ i) {
if (pre[i + len - 1] < i) {
++ res;
}
}
} else {
for (int i = std::min(r, mid + len - 1); i - len + 1 >= l && i >= mid; -- i) {
if (pre[i] < i - len + 1) {
++ res;
}
}
}
return res;
};
std::cout << dfs(dfs, 0, n - 1) << "\n";
}