VP TOYOTA SYSTEMS Programming Contest 2024(AtCoder Beginner Contest 377)
A - Rearranging ABC
点击查看代码
void solve() {
int cnt[26]{};
std::string s;
std::cin >> s;
for (auto & c : s) {
++ cnt[c - 'A'];
}
if (cnt[0] == cnt[1] && cnt[1] == cnt[2] && cnt[2] == 1) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
}
B - Avoid Rook Attack
题意:棋盘上有\(n\)个棋子,每个棋子都能吃到所在行列的格子。问有多少格子不会被吃。
记录被吃的行和列。一个格子不会被吃那么它所在的行列都不会被吃。
点击查看代码
void solve() {
int n = 8;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::vector<int> row(n), col(n);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
if (s[i][j] == '#') {
row[i] = col[j] = 1;
}
}
}
int ans = 0;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
ans += row[i] == 0 && col[j] == 0;
}
}
std::cout << ans << "\n";
}
C - Avoid Knight Attack
题意:棋盘上有\(n\)个棋子,棋子是象棋里马的走法。问有多少格子不会被吃。
把被吃的格子用\(set\)存下来。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
const int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2}, dy[] = {1, 2, 2, 1, -1, -2, -2, -1};
std::set<std::pair<int, int>> s;
for (int i = 0; i < m; ++ i) {
int x, y;
std::cin >> x >> y;
-- x, -- y;
s.insert({x, y});
for (int j = 0; j < 8; ++ j) {
int nx = x + dx[j], ny = y + dy[j];
if (nx < 0 || nx >= n || ny < 0 || ny >= n) {
continue;
}
s.insert({nx, ny});
}
}
std::cout << (i64)n * n - (int)s.size() << "\n";
}
D - Many Segments 2
题意:有\(n\)个区间,求有多少区间不包含其中任意一个区间。值域为\([1, m]\)。
把每个右端点对应的左端点存下来。然后枚举右端点,这个右端点可以往左取到的地方就是前面所有区间的\(l\)的最大值。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int>> R(m + 1);
for (int i = 0; i < n; ++ i) {
int l, r;
std::cin >> l >> r;
R[r].push_back(l);
}
i64 ans = 0;
int l = 0;
for (int r = 1; r <= m; ++ r) {
for (auto & p : R[r]) {
l = std::max(l, p);
}
ans += r - l;
}
std::cout << ans << "\n";
}
E - Permute K times 2
题意:给你一个排列,每次使得\(p_i = p_{p_i}\)。问进行\(k\)次后的排列是什么。
如果按\(i\)向\(p_i\)建边,那么因为是一个排列,每个点恰好有一个出度一个入度,就会形成若干个环。这些环不管操作几次都是这些数。于是我们观察每一个环的变化。跳一次变成\(p_{p_i}\),二次变成\(p_{p_{p_{p_i}}}\),那么发现每个点走了\(2^k\)次。
点击查看代码
void solve() {
int n;
i64 k;
std::cin >> n >> k;
std::vector<int> p(n);
for (int i = 0; i < n; ++ i) {
std::cin >> p[i];
-- p[i];
}
auto power = [&](int a, i64 b, int m) -> int {
int res = 1;
for (; b; b >>= 1, a = (i64)a * a % m) {
if (b & 1) {
res = (i64)res * a % m;
}
}
return res;
};
std::vector<int> st(n), ans(n);
for (int i = 0; i < n; ++ i) {
if (!st[i]) {
int j = i;
std::vector<int> a;
while (!st[j]) {
st[j] = 1;
a.push_back(j);
j = p[j];
}
int m = a.size();
int d = power(2, k, m);
for (int i = 0; i < m; ++ i) {
ans[a[i]] = p[a[(i + d - 1 + m) % m]];
}
}
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] + 1 << " \n"[i == n - 1];
}
}
G - Edit to Match
题意:依次输入\(n\)个字符串,对于第\(i\)个字符串,你可以每次花1的代价删除最后一个字符,或者在最后面加一个字符。问让第\(i\)个字符串和前面某一个字符串相等的最少代价。
建立一棵字典树,对于字典树的每一个节点\(p\),记\(f[p]\)为前缀匹配到了\(p\),还需要往后面加多少字符串可以和已有的字符串相等的最小代价。那么每次插入时到一个节点就看要删几个字符到这个前缀,然后加上\(f[p]\)。同时把插入时遇到的\(p\)存下来,然后从后往前更新。
点击查看代码
const int TrieN = 1e6 + 5;
int trie[TrieN][26], f[TrieN * 26];
struct Trie {
int idx;
Trie() {
idx = 0;
}
int newNode() {
idx += 1;
memset(trie[idx], 0, sizeof trie[idx]);
f[idx] = 1e9;
return idx;
}
int insert(const std::string & s) {
int p = 0;
std::vector<int> a;
int res = 1e9;
int len = s.size();
for (auto & c : s) {
int x = c - 'a';
if (!trie[p][x]) {
trie[p][x] = newNode();
}
p = trie[p][x];
-- len;
res = std::min(res, f[p] + len);
a.push_back(p);
}
int n = a.size();
f[a[n - 1]] = 0;
for (int i = n - 2; i >= 0; -- i) {
f[a[i]] = std::min(f[a[i]], f[a[i + 1]] + 1);
}
return res;
}
};
void solve() {
int n;
std::cin >> n;
Trie tr;
for (int i = 0; i < n; ++ i) {
std::string s;
std::cin >> s;
std::cout << std::min((int)s.size(), tr.insert(s)) << "\n";
}
}

浙公网安备 33010602011771号