本次比赛 A 题还是出了小锅,幸好发现的比较早,最后影响还算不大。
A - ABC202A
签到,不讲。我没想到读假了还能过(
int main() {
int a, b, c;
cin >> a >> b >> c;
cout << 21 - a - b - c;
return 0;
}
B - CF567A
注意到座标是按照从小到大顺序的,那么对于第 $i$ 个人,最小值显然在 $x_{i-1}, x_{i+1}$ 中取到,最大值在 $x_{1}, x_n$ 中取到。
注意特判左右两边。
int main() {
int n;
cin >> n;
vector<int> v(n);
for (auto &vi : v)
cin >> vi;
for (int i = 0; i < n; i++) {
int m1, m2;
if (i == 0) {
m1 = v[1] - v[i], m2 = v[n - 1] - v[i];
} else if (i == n - 1) {
m2 = v[i] - v[0], m1 = v[i] - v[i - 1];
} else {
m1 = min(v[i] - v[i - 1], v[i + 1] - v[i]);
m2 = max(v[i] - v[0], v[n - 1] - v[i]);
}
cout << m1 << " " << m2 << "\n";
}
return 0;
}
C - CF765B
即第 $i$ 个字母第一次出现时,第 $1 \sim i - 1$ 个字母都应该出现过。
int main() {
string s;
cin >> s;
char past = 'a' - 1;
for (auto si : s) {
if (si > past) {
if (si - past != 1) {
cout << "NO";
return 0;
}
past = si;
}
}
cout << "YES";
return 0;
}
D - CF675B
注意到中间那个格子取 $1 \sim n$ 都行,四个角上的数字则是互相牵制的,可以枚举得到。
或者进一步,四个数字之间的相对差值是固定的,答案只能在一个区间中取。我们可以直接计算出这个区间。
int main() {
ll a, b, c, d, n;
cin >> n >> a >> b >> c >> d;
auto il = {a + b, b + d, d + c, c + a};
ll L = max(il) - min(il) + 1, R = n;
cout << n * max<ll>(R - L + 1, 0);
return 0;
}
E - CF660C
我们即需要找到最长的区间,其中 $0$ 的个数不超过 $k$。
可以考虑枚举区间的右端点 $r$。当 $r$ 固定时,设 $[l,r]$ 中 $0$ 的个数是关于 $f(l)$ 的函数,那么 $f(l)$ 显然关于 $l$ 递减。可以使用二分寻找最左边且符合要求的 $l$。时间复杂度 $O(n \log n)$。
更进一步,可以考虑双指针法,时间复杂度 $O(n)$。
int main() {
int n, k;
cin >> n >> k;
vector<int> v(n);
for (int i = 0; i < n; i++) {
cin >> v[i];
}
int L = 0, R = -1, sum0 = 0;
int tl = 0, tr = -1;
for (int i = 0; i < n; i++) {
if (v[i] == 0) {
sum0++;
while (sum0 > k) {
sum0 -= v[tl++] == 0;
}
}
tr = i;
if (tr - tl > R - L) {
L = tl, R = tr;
}
}
int ans = R - L + 1;
cout << ans << "\n";
fill_n(v.begin() + L, ans, 1);
for (auto vi : v) {
cout << vi << " ";
}
return 0;
}
F - CF1208D
注意到最右边,$s_n$ 包含了所有比 $p_n$ 小的数,据此可以直接计算出 $p_n$。
对于 $s_{n-1}$,我们可以排除掉 $p_n$ 的影响,那么 $p_{n-1}$ 也是类似可以直接计算的。如此前推,计算出所有的 $p$。
具体的说,用树状数组维护 $1 \sim n$ 的和,那么 $s_n$ 必然在树状数组中出现。找到 $p_n$ 后,树状数组减去 tr.add(p[n], -p[n])。之后,$s_{n-1}$ 也在树状数组中出现,再减去。以此类推。
即是树状数组上二分,朴素实现的时间复杂度是 $O(n \log^2 n)$,略微优化可以做到 $O(n \log n)$。
template <class T>
struct fwtree_1 {
vector<T> v;
fwtree_1(int n = 0) : v(n) {}
int len() {
return v.size();
}
void add(int i, T x) {
for (; 0 < i && i < len(); i += i & -i)
v[i] += x;
}
T sum(int i) {
T sum = 0;
for (; i > 0; i -= i & -i)
sum += v[i];
return sum;
}
T sum(int l, int r) {
return sum(r) - sum(l - 1);
}
int find(T x) {
int i = 0;
for (int k = 2 << std::__lg(len()); k >= 1; k >>= 1) {
int u = i + k;
if (u < len() && v[u] <= x)
i = u, x -= v[u];
}
return i;
}
};
int main() {
int n;
cin >> n;
fwtree_1<ll> tr(n + 1);
vector<ll> s(n + 1);
for (int i = 1; i <= n; i++) {
cin >> s[i];
tr.add(i, i);
}
vector<int> v(n + 1);
for (int i = n; i >= 1; i--) {
v[i] = tr.find(s[i]) + 1;
tr.add(v[i], -v[i]);
}
for (int i = 1; i <= n; i++)
cout << v[i] << " ";
return 0;
}
G - CF433C
若我们把第 $x$ 页合到第 $y$ 页,那么受影响的只有序列 $a$ 中所有 $x$ 相邻元素。我们可以考虑对 $1 \sim n$ 的所有页码,记录所有与其相邻元素。
假设对于页面 $x$,我们记录了所有相邻元素于 $\{e_x\}$ 中,当页面 $x$ 应该与序列 $\{e_x\}$ 的中位数合并时最优。
获取中位数可以简单的通过排序得到。注意,相邻元素不能和自己相同,否则修改时应注意。
int main() {
int n, m;
cin >> n >> m;
vector<int> v(m);
for (auto &vi : v)
cin >> vi;
ll ans = 0;
for (int i = 0; i < m - 1; i++) {
ans += abs(v[i] - v[i + 1]);
}
vector<vector<int>> e(n + 1);
for (int i = 0; i < m; i++) {
if (i > 0 && v[i - 1] != v[i])
e[v[i]].push_back(v[i - 1]);
if (i < m - 1 && v[i + 1] != v[i])
e[v[i]].push_back(v[i + 1]);
}
ll sum = 0;
for (int i = 1; i <= n; i++) {
if (e[i].empty())
continue;
sort(e[i].begin(), e[i].end());
int mid = e[i][(e[i].size() - 1) / 2];
ll s1 = 0, s2 = 0;
for (auto eii : e[i]) {
s1 += abs(mid - eii);
s2 += abs(i - eii);
}
sum = max(sum, s2 - s1);
}
cout << ans - sum;
return 0;
}
H - ABC227G
这题一点也不数论呀,怎么没人做呢。
注意到
$$ \binom{N}{K} = \frac{N(N-1)\cdots(N-K+1)}{1\cdots (K-1)K} $$
我们可以考虑对于每个质因子,分开计算其贡献。再注意到 $K \leqslant 10^6$,这说明我们需要处理的数最多不会超过 $10^6$ 个,不用被 $N$ 吓到。
处理时也需要一点技巧。不能分别对所有的 $1 \sim K$ 因式分解,我们批量计算时可以仿照筛法,可以直接筛去 $2p,3p,\cdots$。
而对于大于 $\sqrt{N}$ 的质因子,因为这些大的质因子每个数最多只有 $1$ 个,我们可以最后去除。
对于 $1\sim n$ 的计算还可以采用一些优化。回想 LightOJ - Trailing Zeroes (III)
$n!$ 的末尾有几个零?$\Longrightarrow$ $n!$ 有几个因子 $5$?
类似的,可以通过不断对 $p$ 除去直接得到答案,不必真的计算。
using pll = pair<ll, ll>;
const int P = 998244353;
vector<bool> notp;
vector<int> primes;
void Euler(int n) {
notp.resize(n + 1);
auto _ = [&](int i) {
if (!notp[i])
primes.push_back(i);
for (auto pj : primes) {
if (pj > n / i)
break;
notp[i * pj] = true;
if (i % pj == 0)
break;
}
};
_(2), _(3), _(5);
for (int i = 1; i <= n / 6; i++) {
_(i * 6 + 1), _(i * 6 + 5);
}
}
int main() {
ll n, k;
cin >> n >> k;
Euler(max<int>(sqrt(n * 1.0) + 1, k));
vector<ll> v2(k + 1);
for (int i = 1; i <= k; i++) {
v2[i] = n - k + i;
}
ll ans = 1;
for (auto p : primes) {
int cnt = 0;
int tk = k;
while (tk > 0) {
tk /= p, cnt -= tk;
}
for (int i = p - (n - k) % p; i <= k; i += p) {
while (v2[i] % p == 0) {
cnt++, v2[i] /= p;
}
}
ans = ans * (cnt + 1) % P;
}
for (auto v2i : v2)
if (v2i > 1)
ans = ans * 2 % P;
cout << ans;
return 0;
}