AtCoder Beginner Contest 322
A - First ABC 2 (abc322 A)
题目大意
给定一个字符串,找到最先出现ABC
的位置。
解题思路
直接查找判断即可。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
string s;
cin >> n >> s;
int pos = s.find("ABC");
if (pos == string::npos)
pos = -2;
cout << pos + 1 << '\n';
return 0;
}
B - Prefix and Suffix (abc322 B)
题目大意
给定字符串 s
和t
,问s
是不是t
的前缀和后缀。
解题思路
根据前后缀定义判断即可。这里试了下python
神奇的代码
n, m = map(int, input().split(' '))
s = input()
t = input()
prefix = t.startswith(s)
suffix = t.endswith(s)
if prefix and suffix:
print(0)
elif prefix and not suffix:
print(1)
elif not prefix and suffix:
print(2)
else:
print(3)
C - Festival (abc322 C)
题目大意
\(n\)天,有\(m\)天会放烟花。
问每一天,距离未来最近的放烟花的天数。
解题思路
两个双指针一样,一个指针指向当前天数,一个指针指向未来最近的放烟花的天数,两者差就是答案。然后两个指针不断往未来移动。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> day(m);
for (auto& i : day)
cin >> i;
int cur = 0;
for (int i = 1; i <= n; ++i) {
if (i > day[cur])
++cur;
cout << day[cur] - i << '\n';
}
return 0;
}
D - Polyomino (abc322 D)
题目大意
给定三个多米诺骨牌,问能否不重叠地摆成\(4 \times 4\)的方格。
解题思路
数不大,直接暴力搜索。
考虑搜索方式,虽然给了个\(4 \times 4\)的多米诺骨牌的表示形式,但我们就枚举这个 \(4 \times 4\) 的方格的位置。
设想我们的画布就是一个\(4 \times 4\)的方格,然后枚举一个多米诺骨牌盖章左上角的位置(可以在这个画布之外),以及旋转的角度,然后一盖,在该区域的多米诺骨就被保留下来。最后我们就看该区域是否填满了,且没重叠,且无多米诺骨牌在外面。
代码实现里没有真正的盖,只取了盖在画布上的格子,方法类似于
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
array<array<string, 4>, 3> tu;
int cnt = 0;
for (auto& i : tu)
for (auto& j : i) {
cin >> j;
cnt += count(j.begin(), j.end(), '#');
}
array<array<char, 4>, 4> page{};
int dx1[2] = {1, -1};
int dy1[2] = {1, -1};
int dx2[2] = {1, -1};
int dy2[2] = {-1, 1};
auto fit1 = [&](int k, int x, int y, int d) {
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j) {
int nx = x + dx1[d] * i, ny = y + dy1[d] * j;
if (tu[k][i][j] == '#') {
if (nx >= 0 && nx < 4 && ny >= 0 && ny < 4) {
if (page[nx][ny] == '#') {
return false;
}
page[nx][ny] = '#';
} else {
return false;
}
}
}
return true;
};
auto fit2 = [&](int k, int x, int y, int d) {
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j) {
int nx = x + dx2[d] * j, ny = y + dy2[d] * i;
if (tu[k][i][j] == '#') {
if (nx >= 0 && nx < 4 && ny >= 0 && ny < 4) {
if (page[nx][ny] == '#') {
return false;
}
page[nx][ny] = '#';
} else {
return false;
}
}
}
return true;
};
function<bool(int)> ok = [&](int k) {
if (k == 3) {
return true;
}
auto bak = page;
for (int x = -3; x <= 7; ++x)
for (int y = -3; y <= 7; ++y) {
for (int d = 0; d < 2; ++d) {
if (fit1(k, x, y, d)) {
if (ok(k + 1))
return true;
}
page = bak;
if (fit2(k, x, y, d)) {
if (ok(k + 1))
return true;
}
page = bak;
}
}
return false;
};
if (cnt == 16 && ok(0))
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}
上述代码实际上没有真正旋转,在盖章的时候考虑了角度,而旋转\(90,270\)跟\(0,180\)的考虑逻辑不一样,因此代码会比较长,下述代码则真正旋转了矩阵,代码简洁点,不过运行时间会长一点点(指长了\(4ms\))
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
array<array<string, 4>, 3> tu;
int cnt = 0;
for (auto& i : tu)
for (auto& j : i) {
cin >> j;
cnt += count(j.begin(), j.end(), '#');
}
array<array<char, 4>, 4> page{};
auto fit = [&](int k, int x, int y) {
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j) {
int nx = x + i, ny = y + j;
if (tu[k][i][j] == '#') {
if (nx >= 0 && nx < 4 && ny >= 0 && ny < 4) {
if (page[nx][ny] == '#') {
return false;
}
page[nx][ny] = '#';
} else {
return false;
}
}
}
return true;
};
auto rotate = [&](const array<string, 4>& a) {
auto ret = a;
for (int i = 0; i < a.size(); ++i) {
for (int j = 0; j < a.front().size(); ++j)
ret[j][a.size() - i - 1] = a[i][j];
}
return ret;
};
function<bool(int)> ok = [&](int k) {
if (k == 3) {
return true;
}
auto bak = page;
for (int x = -3; x <= 7; ++x)
for (int y = -3; y <= 7; ++y) {
for (int r = 0; r < 4; ++r) {
if (fit(k, x, y)) {
if (ok(k + 1))
return true;
}
tu[k] = rotate(tu[k]);
page = bak;
}
}
return false;
};
if (cnt == 16 && ok(0))
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}
E - Product Development (abc322 E)
题目大意
一个产品,有\(k\)个性能参数,初始为\(0\),要求进行一些提升计划,使得每个性能参数不小于 \(p\),且代价最小。
每个提升计划包含一个代价,以及对这\(k\)个性能参数提升的数值。
解题思路
注意到\(k,p\)最大都只有 \(5\)。考虑搜索状态,我们可以设 \(dp[i][a1][a2][a3][a4][a5]\)表示前 \(i\)个提升计划,使得最终性能参数分别为 \(a1,a2,a3,a4,a5\)的最小代价。转移就考虑当前提升计划选或不选即可。
但这里的 \(p\)不一定是 \(5\),也就是说这个 \(dp\)的维度不是固定的,但由于每一维度的取值只有 \(0 \sim p\)共 \(6\)种情况,最多 \(k=5\)维,因此我们可以把这最后的 \(k\)维压缩成一维的 \(6\)进制数表示。
由于 \(dp[i]\)只依赖于 \(dp[i-1]\),因此第一维可以滚动数组优化掉。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const LL inf = 1e18;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, k, p;
cin >> n >> k >> p;
int base = p + 1;
const int sz = int(pow(base, k));
vector<LL> dp(sz, inf);
dp[0] = 0;
auto dtr = [&](int x) {
vector<int> ret(k);
for (int i = k - 1; i >= 0; --i) {
ret[i] = x % base;
x /= base;
}
return ret;
};
auto tr = [&](const vector<int>& x) {
int ret = 0;
for (int i = 0; i < k; ++i) {
ret = ret * base + x[i];
}
return ret;
};
for (int i = 0; i < n; ++i) {
int c;
cin >> c;
vector<int> x(k);
vector<LL> dp2 = dp;
for (auto& i : x)
cin >> i;
for (int j = 0; j < sz; ++j) {
auto now = dtr(j);
for (int i = 0; i < k; ++i)
now[i] = min(p, now[i] + x[i]);
auto nxt = tr(now);
dp2[nxt] = min(dp2[nxt], dp[j] + c);
}
dp.swap(dp2);
}
LL ans = dp.back();
if (ans == inf)
ans = -1;
cout << ans << '\n';
return 0;
}
F - Vacation Query (abc322 F)
题目大意
给定一个\(01\)串\(s\)。维护两种操作。
- \(1, l,r\),将\(s[l..r]\)的数字翻转。
- \(2,l,r\),问\(s[l..r]\)中最长的连续的\(1\)的长度。
解题思路
不考虑修改,仅考虑查询。
考虑如何求解,对于每个\(r\),我们要找最小的 \(l\)满足 \(sum[r] - sum[l] = r - l\),其中 \(sum[i]\)表示 \(s[1..i]\)中 \(1\)的个数。
但这涉及到特定区间的信息,感觉难以维护。
注意到问的是连续的,还有一种思路是考虑区间信息的合并,即一个区间的最长连续段,要么在左区间,要么在右区间,要么是左区间的后缀和右区间的前缀合并起来。与此相关的其他信息(区间最长连续 \(1\)的前缀 、后缀长度、是否全是\(1\),这些信息都可以合并),因此可以用线段树维护区间的上述信息,对于每个区间答案,合并\(\log\)次区间信息即可得到 。
考虑修改,事实上就是把\(1\)看成 \(0\), \(0\)看成 \(1\)。因此我们分别对 \(0,1\)这两个数维护上述信息,修改时交换一下这两个信息即可。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 5e5 + 8;
class segment {
#define lson root << 1
#define rson root << 1 | 1
struct node {
struct info {
int ans, l, r;
bool all;
} _info[2];
bool flip;
node operator+(const node& o) {
node ret;
ret.flip = 0;
for (int i = 0; i < 2; ++i) {
const auto &L = _info[i], R = o._info[i];
ret._info[i].ans = max({L.ans, R.ans, L.r + R.l});
ret._info[i].l = L.l;
ret._info[i].r = R.r;
if (L.all)
ret._info[i].l = L.l + R.l;
if (R.all)
ret._info[i].r = L.r + R.r;
ret._info[i].all = (L.all && R.all);
}
return ret;
}
void swap_info() { swap(_info[0], _info[1]); }
int ans() { return _info[1].ans; }
} a[N << 2];
public:
void build(int root, int l, int r, const string& s) {
if (l == r) {
a[root].flip = 0;
for (int i = 0; i < 2; ++i) {
auto& info = a[root]._info[i];
info.l = info.r = info.all = info.ans = (s[l - 1] == '0' + i);
}
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid, s);
build(rson, mid + 1, r, s);
a[root] = a[lson] + a[rson];
}
void pushdown(int root) {
if (a[root].flip) {
a[lson].flip ^= 1;
a[lson].swap_info();
a[rson].flip ^= 1;
a[rson].swap_info();
a[root].flip = 0;
}
}
void update(int root, int l, int r, int ll, int rr) {
if (ll <= l && r <= rr) {
a[root].flip ^= 1;
a[root].swap_info();
return;
}
pushdown(root);
int mid = (l + r) >> 1;
if (ll <= mid)
update(lson, l, mid, ll, rr);
if (rr > mid)
update(rson, mid + 1, r, ll, rr);
a[root] = a[lson] + a[rson];
}
node query(int root, int l, int r, int ll, int rr) {
if (ll <= l && r <= rr) {
return a[root];
}
pushdown(root);
int mid = (l + r) >> 1;
node L, R;
if (ll <= mid)
L = query(lson, l, mid, ll, rr);
if (rr > mid)
R = query(rson, mid + 1, r, ll, rr);
if (ll > mid)
return R;
else if (rr <= mid)
return L;
else
return L + R;
}
} seg;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
string s;
cin >> s;
seg.build(1, 1, n, s);
while (q--) {
int c, l, r;
cin >> c >> l >> r;
if (c == 1) {
seg.update(1, 1, n, l, r);
} else {
auto ans = seg.query(1, 1, n, l, r);
cout << ans.ans() << '\n';
}
}
return 0;
}
G - Two Kinds of Base (abc322 G)
题目大意
给定\(n,x\),问有多少组 \((s,a,b)\),满足下述条件:
- \(a,b \leq n\)
- \(s\)是一个序列,长度随意, \(s_i < \min(10, a, b), s_1 \neq 0\)
- 设\(s\)是\(a\)进制的表示形式时,其值为\(sa\), \(b\)进制的表示形式时,其值为 \(sb\),要求 \(sa - sb = x\)
解题思路
<++>
神奇的代码