AtCoder Beginner Contest 045
A - Trapezoids
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int a, b, h;
cin >> a >> b >> h;
cout << (a + b) * h / 2;
return 0;
}
B - Card Game for Three (ABC Edit)
\(p[]\) 为当前牌堆顶指针。
\(\rm k\) 记录当前要从哪个牌堆抽牌(因为Alice先开始,所以初值为0)
看当前牌堆顶指针是否已经指到 \(len[k]\) 处,如果是则说明当前牌堆已空。
否则的话就把 \(k\) 指向当前牌堆顶的字母所指的牌堆,同时当前牌堆顶指针后移一位。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int p[3];
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
string s[3];
cin >> s[0] >> s[1] >> s[2];
int len[3];
len[0] = s[0].size(), len[1] = s[1].size(), len[2] = s[2].size();
int k = 0;
while (1) {
if (p[k] == len[k]) {
cout << char(k + 'A');
break;
}
k = s[k][p[k]++] - 'a';//这里 p[k]++ 必须写在里面,否则k更新后再改变的就是新牌堆的堆顶指针了
}
return 0;
}
C - Many Formulas
法一:\(\rm DFS\) 枚举所有 + 号的可能位置,将子串的大小相加。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
vector<vector<string>> get(string s) {
int n = s.size();
vector<vector<string>> ans;
vector<string> p;
function<void(int)> dfs = [&](int i) {
if (i == n) {
ans.push_back(p);
return;
}
for (int j = i; j < n; j++) {
p.push_back(s.substr(i, j - i + 1));
dfs(j + 1);
p.pop_back();
}
};
dfs(0);
return ans;
}
i64 calc(string s) {
i64 ans = 0, p = 1;
int n = s.size();
for (int i = n - 1; i >= 0; i--) {
ans += p * (s[i] - '0');
p *= 10;
}
return ans;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
string s;
cin >> s;
vector<vector<string>> t = get(s);
i64 ans = 0;
for (auto v : t) {
for (auto i : v) {
ans += calc(i);
}
}
cout << ans;
return 0;
}
法二、位运算
设字符串的长度为 \(n\),则有 \(n-1\) 个空隙。每个空隙均有选或不选两种可能,共有 \(2^{n-1}\) 种情况,如字符串为 \(abc\),\(10\) 就表示 \(a\)、\(bc\),\(01\) 表示 \(ab\),\(c\)。依次枚举并计算。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
string s;
cin >> s;
int n = s.size();
i64 ans = 0;
for (int i = 0; i < (1 << (n - 1)); i++) {
int m = i;
i64 p = 1;
for (int j = n - 1; j >= 0; j--) {//从后往前计算
ans += p * (s[j] - '0');
p *= 10;
if ((m & 1) == 1) {//当前二进制数的最后一位为1就说明有断点,将 p 置为 1
p = 1;
}
m >>= 1;//当前二进制数右移一位
}
}
cout << ans;
return 0;
}
D - Snuke's Coloring
本来的想法是用二维数组模拟,但发现 \(H\) 和 \(W\) 的数据范围为 \(10^9\),双重循环肯定会 TLE,而注意到 \(N\) 为 \(10^5\) 数量级,所以可以考虑每个格子对其所在的3x3区域的贡献。用区域中心的点坐标代表这个区域,可知中心点的坐标合法范围是 \((1 \sim H) and (1 \sim W)\)。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
i64 H, W, N;
cin >> H >> W >> N;
vector<i64> ans(10);
map<pair<int, int>, int> cnt;//也可改为map<array<int, 2>, int>,表示每个3x3区域被覆盖的次数
ans[0] = (H - 2) * (W - 2);//初始时所有3x3区域都未被覆盖
for (int i = 0; i < N; i++) {
int a, b;
cin >> a >> b;
for (int j = -1; j <= 1; j++) {
for (int k = -1; k <= 1; k++) {
int x = a + j, y = b + k;//找区域中心点
if (x > 1 && x < H && y > 1 && y < W) {//如果范围合法,就说明当前坐标对(x,y)区域有贡献
int now = ++cnt[{x, y}];//先获取当前区域的覆盖次数,然后贡献+1
ans[now]++, ans[now - 1]--;//当前覆盖次数的区域数量+1,未更新时的区域数量-1
}
}
}
}
for (auto i : ans) cout << i << "\n";
return 0;
}

浙公网安备 33010602011771号