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;
}
posted @ 2024-08-16 12:34  胖柚の工作室  阅读(17)  评论(0)    收藏  举报