Educational Codeforces Round 66 (Rated for Div. 2) A~F
A - From Hero to Zero
模拟。
能除 \(k\) 直接除 \(k\),否则减掉余数部分。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
i64 n, k;
std::cin >> n >> k;
i64 ans = 0;
while(n){
while(n && n % k == 0){
n /= k;
ans += 1;
}
ans += n % k;
n -= n % k;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
B - Catch Overflow!
模拟。
用栈维护每次操作的和,碰到 end 就把前一个 for 到现在的和取出来乘以该次的循环次数,溢出直接退出即可。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
i64 x = (1LL << 32) - 1;
std::stack<std::array<i64,2>> st;
std::vector<std::array<int,2>> num;
for(int i = 0; i < n; i += 1) {
std::string s;
std::cin >> s;
if(s == "add") {
st.push({1, i});
} else if(s == "for") {
int a;
std::cin >> a;
num.push_back({a, i});
} else {
i64 tmp = 0;
while(st.size() && st.top()[1] >= num.back()[1]) {
tmp += st.top()[0];
st.pop();
}
auto k = num.back()[0];
num.pop_back();
if(tmp * k > x) {
std::cout << "OVERFLOW!!!\n";
return 0;
}
st.push({tmp * k, i});
}
}
i64 now = 0;
while(st.size()) {
now += st.top()[0];
st.pop();
if(now > x) {
std::cout << "OVERFLOW!!!\n";
return 0;
}
}
std::cout << now << "\n";
return 0;
}
C - Electrification
枚举。
\(|a_i-x|\) 表示 \(a_i\) 离 \(x\) 的距离,那么找离 \(k+1\) 近的距离就是离它第 \(k+1\) 近的点,则前面 \(k\) 个点都是 \(x\) 两周形成了一段连续的区间。
假设离 \(x\) 最近的 \(k\) 个点为 \(a_i,..,a_{i+k-1}\),那么第 \(k+1\) 小的距离就在两端,即 \(f_k(x)=\min(x-a_{i-1},a_{i+k}-x)\),也就是说可以枚举 \(k+1\) 长度的区间,那么这个第 \(k+1\) 点要么是 \(a_i\),要么是 \(a_{i+k}\),选取最小的一个即可,那么位置就是 \(a_i+\frac{a_{i+k}-a_i}2\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
int ans = -1, l = INT_MAX;
std::vector<int> a(n);
for(int i = 0; i < n; i += 1) {
std::cin >> a[i];
if(i >= k) {
int j = i - k, d = (a[i] - a[j]);
if(d < l){
l = d;
ans = a[j] + d / 2;
}
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
D - Array Splitting
数学。
记 \(S_k=\sum_{i=k}^na_i\) 表示为从 \(k\) 到 \(n\) 的后缀和,\(p_i\) 表示第 \(i\) 个子数组的起点下标,显然有 \(p_1=1,p_i<p_{i+1}\)。
那么 \(\sum\limits_{i=1}^{n} (a_i \cdot f(i))=1\cdot(S_{p_1}-S_{p_2})+2\cdot(S_{p_2}-S_{p_3})+\cdots+k\cdot (S_{p_k}-0)\),拆开得 \(S_{p_1}+(2-1)S_{p_2}+(3-2)S_{p_3}+\cdots+(k-(k-1))S_{p_k}=S_{p_1}+S_{p_2}+S_{p_3}+\cdots+S_{p_k}\),显然,除了 \(p_1\) 是固定的,其他的 \(S_{p_2\sim p_k}\) 是可以任意选择的,所以预处理 \(S_i\),选取前 \(k-1\) 大的 \(S_{2\sim n}\) 再加上 \(S_1\) 即可。
参考[1]。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for(int i = 0;i < n;i += 1){
std::cin >> a[i];
}
std::vector<i64> p(n);
for(int i = n - 1;i >= 0;i -= 1){
p[i] = a[i];
if(i < n - 1){
p[i] += p[i + 1];
}
}
sort(p.begin() + 1, p.end(), std::greater<>());
i64 ans = 0;
for(int i = 0;i < k;i += 1){
ans += p[i];
}
std::cout << ans << "\n";
return 0;
}
E - Minimal Segment Cover
倍增。
记 \(f_{i,k}\) 为从 \(i\) 跑了 \(2^k\) 步最远到达的距离,那么初始可以用指针维护每个坐标能到达的最远距离即可,不能到达的用 \(-1\) 表示。
最后判断的时候先判断 \(f_{i,k}<r\)再去跳,不断地逼近 \(r\),判断大于等于 \(r\) 可能会一次性跳很多之类的,最后跳到 \(x\) 位置,再看 \(x\) 能不能到达 \(r\),能的话再单独跳一次即可。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
int max = 0;
std::vector<std::array<int,2>> a(n);
for(auto &[l, r] : a) {
std::cin >> l >> r;
max = std::max(max, r);
}
sort(a.begin(), a.end());
int j = 0, now = -1;
std::vector<std::array<int,21>> f(max + 1);
for(int i = 0; i <= max; i += 1) {
if(i >= now) {
now = -1;
}
while(j < n && i >= a[j][0]) {
now = std::max(now, a[j][1]);
j += 1;
}
f[i][0] = now;
}
for(int j = 1; j <= std::__lg(max) + 1; j += 1) {
for(int i = 0; i <= max; i += 1) {
if(f[i][j - 1] == -1) {
f[i][j] = -1;
continue;
}
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
while(m --) {
int l, r;
std::cin >> l >> r;
if(r > max) {
std::cout << "-1\n";
continue;
}
int ans = 0;
for(int i = std::__lg(max) + 1; i >= 0; i -= 1) {
if(f[l][i] < r && ~f[l][i]) {
l = f[l][i];
ans += 1 << i;
}
}
if(f[l][0] == -1) {
std::cout << "-1\n";
continue;
}
if(f[l][0] >= r) {
ans += 1;
}
std::cout << ans << "\n";
}
return 0;
}
F - The Number of Subpermutations
异或哈希。
一个区间 \([l,r]\) 要满足是一个 \(1\sim r-l+1\) 的排列,有两个条件可以确定即这个区间所有的数都不重复,并且区间最大值为 \(r-l+1\)。
对 \(1\sim n\) 都赋值一个随机数,再维护一个前缀异或和就可以快速的判定一个区间是否满足要求。
具体地,从 \(1\) 向两边扩展,维护扩展时的最大值 \(x\),如果满足 \([i,i-x+1]\) 的异或和是上面 \(x\) 的前缀异或和即为找到一个答案;可以先从 \(1\) 向右找,然后翻转 \(a\) 再进行一遍即可,最后减掉多算的 \(1\)。
其他做法线段树加单调栈,分治,可以参考[2]。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> val(n + 1), a(n), c(n + 1);
for(int i = 0; i < n; i += 1) {
std::cin >> a[i];
c[i + 1] = val[i + 1] = rng();
c[i + 1] ^= c[i];
}
i64 ans = 0;
auto work = [&]()->void{
std::vector<int> p(n + 1);
int max = 0;
for(int i = 0; i < n; i += 1) {
p[i + 1] = val[a[i]];
p[i + 1] ^= p[i];
if(a[i] == 1) {
max = 1;
}
max = std::max(max, a[i]);
if(i + 1 >= max && (p[i + 1] ^ p[i + 1 - max]) == c[max]) {
ans += 1;
}
}
};
work();
reverse(a.begin(), a.end());
work();
ans -= std::count(a.begin(), a.end(), 1);
std::cout << ans << "\n";
return 0;
}

浙公网安备 33010602011771号