AtCoder Beginner Contest 407 - AtCoder

C

  1. 一定需要 $n$ 次1操作来将 $t$ 变为 $n$ 长度
  2. $t$ 的下标小的位置得到的 2操作一定比下标大的位置更多

把一轮称为某位置执行10次1操作后重新从0开始累加。因此,如果有 $s[i]<s[i+1]$,则必然是 $s[i]$ 增加10之后的新一轮

统计最少需要经过多少轮即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
string s;
cin >> s;
int n = s.size();
i64 ans = 0;
for (int i = n - 2; i >= 0; i--) {
if (s[i] < s[i + 1]) {
ans += 10;
}
}
cout << ans + n + s[0] - '0' << "\n";
return 0;
}

D

搜索题

对于每个位置 $pos$,如果已经被占用,则移动到下一个位置,否则查看能否在当前位置横放或者竖放,使用一个 $st$ 进行状态压缩,遍历所有位置即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
cin >> n >> m;
std::vector<std::vector<i64>> g(n, std::vector<i64>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> g[i][j];
}
}
i64 ans = 0;
auto dfs = [&](auto && dfs, int pos, int st, i64 xorsum = 0) {
if (pos == n * m) {
ans = std::max(ans, xorsum);
return;
}
int row = pos / m, col = pos % m;
if (st >> pos & 1) {
dfs(dfs, pos + 1, st, xorsum);
} else {
dfs(dfs, pos + 1, st, xorsum ^ g[row][col]);
if (row + 1 < n && !(st >> (pos + m) & 1)) {
dfs(dfs, pos + 1, st | (1 << pos) | (1 << (pos + m)), xorsum);
}
if (col + 1 < m && !(st >> (pos + 1) & 1)) {
dfs(dfs, pos + 1, st | (1 << pos) | (1 << (pos + 1)), xorsum);
}
}

};
dfs(dfs, 0, 0);
cout << ans << "\n";
return 0;
}

E

反悔贪心

什么是合法的括号序列?对于任意位置 $i$,对于它的前缀,)的数量 $\le$ (的数量,那么它就是一个合法的括号序列

将问题转化一下,也就是求位置)之和 $sum$ 的最小值,再用总和减去这个最小值

我们枚举位置 $i$,并假设将它加入)之中,$sum$ 加上当前位置的值,如果)计数大于 $\lfloor\frac{i}{2}\rfloor$,则表明当前括号序列不合法,从 $i$ 之前位置弹出一个最大数,用 $sum$ 减去这个最大数,表示将这个最大数位置变为(,这样一直枚举到位置 $n$,则可得到最小的)位置数字之和并且保证之前所有位置的括号序列均合法。

使用优先队列寻找最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve() {
int n;
cin >> n;
std::vector<i64> a(n * 2);
for (auto &x : a) {
cin >> x;
}
std::priority_queue<i64> pq;
i64 sum = 0;
int siz = 0;
for (int i = 0; i < n * 2; i++) {
pq.push(a[i]);
sum += a[i];
siz++;
if (siz > (i + 1) / 2) {
sum -= pq.top();
pq.pop();
siz--;
}
}
cout << std::accumulate(a.begin(), a.end(), 0ll) - sum << "\n";
}

F

使用单调栈计算 $a[i]$ 能成为长度为 $len$ 的序列的最大值的次数,累加其贡献,主要分为以下三种情况:

假设 $i$ 位置左侧第一个比 $a[i]$ 大的位置为 $l[i]$,同理 $r[i]$,$mx$ 为 $max(l[i],r[i])$,同理 $mn$:

  1. 对于 $len=1,2,…,mn-1$,$a[i]$ 对 $len$ 的贡献次数$=len$
  2. 对于 $len=mn,mn+1,…,mx-1$,$a[i]$ 对 $len$ 的贡献次数为 $mn\cdot len$
  3. 对于 $len=mx,mx+1,…,r[i]+l[i]-1$,$a[i]$ 对 $len$ 的贡献次数为 $mn\cdot a[i],(mn-1)\cdot a[i],(mn-2)\cdot a[i]…$

使用二阶差分即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
cin >> n;
std::vector<int> a(n);
for (int &x : a) {
cin >> x;
}
std::stack<int> stk;
std::vector<int> l(n), r(n);
for (int i = 0; i < n; i++) {
while (!stk.empty() && a[stk.top()] < a[i]) {
stk.pop();
}
l[i] = stk.empty() ? -1 : stk.top();
stk.push(i);
}
while (!stk.empty()) {
stk.pop();
}
for (int i = n - 1; i >= 0; i--) {
while (!stk.empty() && a[stk.top()] <= a[i]) {
stk.pop();
}
r[i] = stk.empty() ? n : stk.top();
stk.push(i);
}
std::vector<i64> b(n + 3);
auto add = [&](int h, int t, i64 k, i64 d) -> void {
if (t < h) return;
b[h] += k;
b[h + 1] += d - k;
b[t + 1] -= k + (t - h + 1) * d;
b[t + 2] += k + (t - h) * d;
};
for (int i = 0; i < n; i++) {
int le = i - l[i], ri = r[i] - i;
int mn = std::min(le, ri), mx = std::max(le, ri), len = ri + le - 1;
add(1, mn - 1, a[i], a[i]);
add(mn, mx - 1, 1ll * mn * a[i], 0);
add(mx, len, 1ll * mn * a[i], -a[i]);
}
for (int i = 1; i <= n; i++) {
b[i] += b[i - 1];
}
for (int i = 1; i <= n; i++) {
b[i] += b[i - 1];
cout << b[i] << "\n";
}
return 0;
}