Codeforces Global Round 21
A
只需要让最大值最大,\(\and\) 过后 \(z\) 不会变大,所以最优策略一定是 \(z\) 或上 \(a\) 中的某个数。
B
判掉答案是 \(0\) 和 \(1\) 的情况,剩下的情况序列中一定有 \(0\),只需要全局取一遍 mex,这样所有数都变成了非 \(0\) 数,然后再全局取一遍 mex 即可,所以答案是 \(2\)。
C
把 \(a\) 和 \(b\) 都按 \(m\) 拆到不能再拆后直接判相等即可。
D
solution 1:
官方题解做法,稍微要点脑子。
显然从 \(1\) 到 \(n\) 不管怎么走一定会路过 \([1,n]\) 最大值所在位置 \(p\)(即 \(a_p=n\))。于是只需计算 \(1\) 走到 \(p\) 与 \(p\) 走到 \(n\) 的最短路。
\(1\) 走到 \(p\) 需要路过 \([1,p]\) 最小值所在位置 \(p^{\prime}\),而 \(p^{\prime}\) 可以一步跳到 \(p\),只需计算 \(1\) 走到 \(p^{\prime}\) 的最短路。
\(1\) 走到 \(p^{\prime}\) 又需要路过 \([1,p^{\prime}]\) 最大值所在位置 \(p^{\prime\prime}\),只需计算 \(1\) 走到 \(p^{\prime\prime}\) 的最短路。
\(p\) 走到 \(n\) 是同理的。
于是只需维护前缀后缀最大最小值所在位置即可。非常优美。
#include <cstdio>
int a[250005], premx[250005], premn[250005], sufmx[250005], sufmn[250005];
int main() {
int _;
scanf("%d", &_);
while (_ --) {
int n, ans = 0, pos, d;
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) scanf("%d", a + i);
premx[1] = premn[1] = 1, sufmx[n] = sufmn[n] = n;
for (int i = 2; i <= n; ++ i) {
if (a[i] > a[premx[i - 1]]) premx[i] = i;
else premx[i] = premx[i - 1];
if (a[i] < a[premn[i - 1]]) premn[i] = i;
else premn[i] = premn[i - 1];
}
for (int i = n - 1; i; -- i) {
if (a[i] > a[sufmx[i + 1]]) sufmx[i] = i;
else sufmx[i] = sufmx[i + 1];
if (a[i] < a[sufmn[i + 1]]) sufmn[i] = i;
else sufmn[i] = sufmn[i + 1];
}
d = 0, pos = premx[n];
while (pos != 1) pos = (d ? premx[pos] : premn[pos]), d ^= 1, ++ ans;
d = 0, pos = premx[n];
while (pos != n) pos = (d ? sufmx[pos] : sufmn[pos]), d ^= 1, ++ ans;
printf("%d\n", ans);
}
return 0;
}
solution 2:
赛时的无脑做法,B题-3调了半天才发现答案不超过 \(2\),导致后面很慌,所以看完题想到这个无脑做法直接开始写了。
首先显然每次贪心地跳到最右边能跳的位置是最优的(废话)。
这个跳的过程搞个单调栈求出每个数右边第一个比它大/小的数的位置,加上st表就能跳了。
\(O(n\log n)\),非常粗暴而又丑陋。由于太慌连续两次数组开小白送两发罚时(外加10min)。
#include <cstdio>
#include <cstring>
inline int min(const int x, const int y) {return x < y ? x : y;}
int a[250005], st1[250005][20], st2[250005][20], Log[250005], rmn[250005], rmx[250005], stk[250005], top;
inline int querymin(int l, int r) {
int k = Log[r - l + 1];
return a[st1[l][k]] < a[st1[r - (1 << k) + 1][k]] ? st1[l][k] : st1[r - (1 << k) + 1][k];
}
inline int querymax(int l, int r) {
int k = Log[r - l + 1];
return a[st2[l][k]] > a[st2[r - (1 << k) + 1][k]] ? st2[l][k] : st2[r - (1 << k) + 1][k];
}
int main() {
int _;
scanf("%d", &_);
while (_ --) {
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) scanf("%d", a + i), st1[i][0] = st2[i][0] = i;
for (int i = 2; i <= n; ++ i) Log[i] = Log[i >> 1] + 1;
for (int j = 1; j < 20; ++ j)
for (int i = 1; i + (1 << j) - 1 <= n; ++ i) {
if (a[st1[i][j - 1]] < a[st1[i + (1 << j - 1)][j - 1]]) st1[i][j] = st1[i][j - 1];
else st1[i][j] = st1[i + (1 << j - 1)][j - 1];
if (a[st2[i][j - 1]] > a[st2[i + (1 << j - 1)][j - 1]]) st2[i][j] = st2[i][j - 1];
else st2[i][j] = st2[i + (1 << j - 1)][j - 1];
}
top = 0;
for (int i = 1; i <= n; ++ i) {
while (top && a[stk[top]] > a[i]) rmn[stk[top --]] = i;
stk[++ top] = i;
}
while (top) rmn[stk[top --]] = n + 1;
for (int i = 1; i <= n; ++ i) {
while (top && a[stk[top]] < a[i]) rmx[stk[top --]] = i;
stk[++ top] = i;
}
while (top) rmx[stk[top --]] = n + 1;
for (int i = 1; i < n;)
if (a[i] < a[i + 1]) i = querymax(i, rmn[i] - 1), ++ ans;
else i = querymin(i, rmx[i] - 1), ++ ans;
printf("%d\n", ans);
}
return 0;
}
E
草稿本上自己画一画就能发现这玩意儿就是个杨辉三角也就是组合数。
由于保证了 \(a_i\) 单调不增,所以答案就是 \(\sum\limits^n_{i=0}\tbinom{i+1+a_i-1}{a_i-1}\)。
比做D的时候还要慌,导致没取模白给一发,预处理数组没乘二白给一发,罚时上天,-7,被一堆4题人吊打。
#include <cstdio>
const int mod = 1e9 + 7;
int a[400005], fact[400005], inv[400005];
inline int qpow(int a, int b) {
int ret = 1;
while (b) {
if (b & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod, b >>= 1;
}
return ret;
}
inline int C(int n, int m) {
return 1ll * fact[n] * qpow(fact[m], mod - 2) % mod * qpow(fact[n - m], mod - 2) % mod;
}
int main() {
fact[0] = 1;
for (int i = 1; i <= 400004; ++ i) fact[i] = 1ll * fact[i - 1] * i % mod;
int n, ans = 0;
scanf("%d", &n);
for (int i = 0, x; i <= n; ++ i) scanf("%d", &x), ans = (ans + C(i + 1 + x - 1, x - 1)) % mod;
printf("%d", ans);
return 0;
}