Codeforces Round 753 (Div. 3)
A
因为把26打成25还WA了一发
void solve() {
std::string s, a;
std::cin >> s >> a;
std::unordered_map<int, int> w;
for (int i = 0; i < 26; i++) {
w[s[i]] = i;
}
int ans = 0;
for (int i = 1; i < a.size(); i++) {
ans += std::abs(w[a[i]] - w[a[i - 1]]);
}
std::cout << ans << "\n";
}
B
一个简单分类讨论,思路想假了想了太久,而且从第二位开始讨论的
实际上从第一位开始讨论更方便,因为只用考虑最后三个数:
$设m = 4_floor(n)$
$x是奇数,依次+m+1,−1,-m-4$
$x是偶数,依次-m-1,+1,+m+4$
void solve() {
ll x, n;
std::cin >> x >> n;
int m = (n - 1) % 4;
if (x & 1) {
x += 1 + (n - 1) / 4 * 4;
ll top = (n - 1) / 4 * 4 + 1;
switch (((n - 1) % 4 + 4) % 4) {
case 3:
x += top + 3;
case 2:
x -= top + 2;
case 1:
x -= top + 1;
}
} else {
x += -1 - (n - 1) / 4 * 4;
ll top = (n - 1) / 4 * 4 + 1;
switch (((n - 1) % 4 + 4) % 4) {
case 3:
x -= top + 3;
case 2:
x += top + 2;
case 1:
x += top + 1;
}
}
std::cout << x << "\n";
}
C
按题意,选最小的最优
对数组排序后容易发现答案只可能是两个相邻元素的差,或者是原始最小
void solve() {
int n;
std::cin >> n;
std::vector<ll> a(n + 1);
ll max = -1e14;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
std::sort(a.begin() + 1, a.end());
for (int i = 1; i <= n; i++) {
max = std::max(max, a[i] - a[i - 1]);
}
std::cout << max << "\n";
}
D
直觉是能增加的尽量占用大的位置,如果不能放就会多出一个数不能满足$permutation$
于是排序之后,要满足条件的最佳方案(也是必要条件)是最大数能够满足从$n$到$n-数组个数$的$permutation$
对能减少的数也一样
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (auto &i : a) {
std::cin >> i;
}
std::string s;
std::cin >> s;
std::priority_queue<int> r;
std::priority_queue<int, std::vector<int>, std::greater<>> b;
for (int i = 0; i < n; i++) {
if (s[i] == 'R') {
r.push(a[i]);
} else {
b.push(a[i]);
}
}
int i;
for (i = n; i && !r.empty(); i--) {
if (r.top() > i) {
std::cout << "NO\n";
return;
} else {
r.pop();
}
}
for (int j = 1; j <= i && !b.empty(); j++) {
if (b.top() < j) {
std::cout << "NO\n";
return;
} else {
b.pop();
}
}
std::cout << "YES\n";
}
E
考虑整条路径,在矩形内的才是有意义的,而且一定可以找到一条不掉出边界的路径,满足能取到最大值,可以二分(额额,因为操作是有序的,所以直接维护最大边界就可以,不用二分)
枚举有序步骤的终点,假设从$(0, 0)$开始,记录最大和最小的$x, y$边界,最后再还原回去就行
void solve() {
int n, m;
std::string s;
std::cin >> n >> m >> s;
int lo = 0, hi = s.size() - 1;
int ansX = 1, ansY = 1;
while (lo < hi) {
int mid = (lo + hi + 1) / 2;
int x = 0, y = 0;
int maxX = 0, minX = 0, maxY = 0, minY = 0;
for (int i = 0; i < mid; i++) {
if (s[i] == 'L') {
y--;
} else if (s[i] == 'R') {
y++;
} else if (s[i] == 'U') {
x--;
} else {
x++;
}
maxX = std::max(x, maxX);
minX = std::min(x, minX);
maxY = std::max(y, maxY);
minY = std::min(y, minY);
}
if (maxX - minX < n && maxY - minY < m) {
lo = mid;
ansX = -minX + 1;
ansY = -minY + 1;
} else {
hi = mid - 1;
}
}
std::cout << ansX << " " << ansY << "\n";
}
F
思路是记录所有点开始的最长路径长度来更新答案长度和坐标
对于没有经过重复点的路径,直接记忆化搜索
对于经过重复点的路径,可以发现重复点到自身路径形成一个环,且路径上每一个点出发的最长路径就是这个环的长度
所有的路径都可以变成一条非环的路径长度加上一条环的长度,也可以记忆化
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::string> mov(n);
for (auto &s : mov) {
std::cin >> s;
}
std::vector cnt(n, std::vector<int>(m));
std::vector vis(n, std::vector<bool>(m));
int x, y, ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!vis[i][j]) {
std::vector<PII> s;
int ni = i, nj = j;
while (ni >= 0 && ni < n && nj >= 0 && nj < m && !vis[ni][nj]) {
vis[ni][nj] = true;
s.emplace_back(ni, nj);
auto t = mov[ni][nj];
if (t == 'U') {
ni--;
} else if (t == 'D') {
ni++;
} else if (t == 'L') {
nj--;
} else {
nj++;
}
}
if (ni < 0 || ni >= n || nj < 0 || nj >= m) {
for (int k = 0; k < s.size(); k++) {
auto [X, Y] = s[k];
cnt[X][Y] = s.size() - k;
}
} else if (cnt[ni][nj]) {
s.emplace_back(ni, nj);
for (int k = s.size() - 2; k >= 0; k--) {
auto [nX, nY] = s[k + 1];
auto [X, Y] = s[k];
cnt[X][Y] = cnt[nX][nY] + 1;
}
} else {
auto p = std::find(s.begin(), s.end(), std::make_pair(ni, nj)) - s.begin();
for (int k = s.size() - 1, len = s.size() - p; k >= 0; k--) {
auto [X, Y] = s[k];
if (k >= p) {
cnt[X][Y] = len;
} else {
auto [nX, nY] = s[k + 1];
cnt[X][Y] = cnt[nX][nY] + 1;
}
}
}
}
if (cnt[i][j] > ans) {
ans = cnt[i][j];
x = i;
y = j;
}
}
}
std::cout << x + 1 << " " << y + 1 << " " << ans << "\n";
}
G
给你两个数组,对于两个数组每个位置减去的数的和一定为$m$,让两个操作之后的自然数组的差最小
如果$m$足够大,必存在一个位置使得两个数组的差为$0$或$1$,否则逼近
最终一个数组的状态决定另外一个的状态,只考虑一个数组,对于每次减操作,存在可能的上下限
先假设全取下限,然后尽量朝目标逼近即可
void solve() {
int n, m;
std::cin >> n >> m;
ll asum = 0, bsum = 0, sum = 0;
std::vector<int> a(n), b(n);
std::vector<ll> max(n), min(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i] >> b[i];
asum += a[i];
bsum += b[i];
max[i] = std::min(a[i], m);
min[i] = std::max(m - b[i], 0);
sum += a[i] - max[i];
}
ll all = (asum + bsum - 1ll * n * m), ave = all / 2;
std::vector<ll> ans(n);
for (int i = 0; i < n; i++) {
ll d = std::min(std::max(ave - sum, 0ll), max[i] - min[i]);
ans[i] = max[i] - d;
sum += d;
}
std::cout << std::abs(2 * sum - all) << "\n";
for (auto d : ans) {
std::cout << d << " " << m - d << "\n";
}
}
H
给任意个$a,b,m$,$m$是$a,b$减去的总和
使得$(a,b)$的种类尽量少
相同的$(a,b)$的性质有$a+b-m$相同,$a$相同($b$相同由a相同决定,少一个自由度)
首先用 std::map
维护$a+b-m$相同的对,然后和G一样维护$a_{max}$和$a_{min}$
$[a_{max},a_{min}]$有相交就可以变成同一种种类,经典区间合并问题
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n), b(n), m(n);
std::map<int, std::vector<std::array<int, 5>>> M;
for (int i = 0; i < n; i++) {
std::cin >> a[i] >> b[i] >> m[i];
int l = std::max(0, a[i] - m[i]), r = a[i] - std::max(0, m[i] - b[i]);
M[a[i] + b[i] - m[i]].push_back({l, r, i, a[i], m[i]});
}
std::vector<PII> d(n);
int ans = 0;
for (auto [sum, v] : M) {
std::sort(v.begin(), v.end(), [](auto &l, auto &r) {
return l[1] < r[1];
});
int ed = -1e9, lst = 0;
for (int i = 0; i < v.size(); i++) {
auto [l, r, id, a, m] = v[i];
if (l > ed) {
ed = r;
ans++;
}
d[id].first = a - ed;
d[id].second = m - d[id].first;
}
}
std::cout << ans << "\n";
for (int i = 0; i < n; i++) {
std::cout << d[i].first << " " << d[i].second << "\n";
}
}