Technocup 2019 - Elimination Round 2 解题报告
对应场次为 CF1031
A. Golden Plate
gild 镀金
题目大意
给定一个 \(n \times m (1 \le n, m \le 100)\) 的矩形 你需要铺 \(k\) 圈黄金地砖
其中最外面那圈为第一层 然后隔一层再铺一圈为第二层 就像这样

显然 第 \(i\) 层地砖的长宽分别为 \(n - 4 \times (i - 1), m - 4 \times (i - 1)\) (题干给的)
你需要计算一共要铺多少个地砖
保证 \(k\) 圈地砖都能铺上
Solution
通过观察不难发现 一个 \(n \times m\) 的矩形外层有 \((n + m - 2) \times 2\) 个地砖
然后用题干中给的式子计算每一层的长宽即可
点击查看代码
/*
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
int xr = 0, F = 1;
char cr;
while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
while (cr >= '0' && cr <= '9')
xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
return xr * F;
}
void write(ll x) {
char ws[51];
int wt = 0;
if (x < 0) putchar('-'), x = -x;
do {
ws[++wt] = x % 10 + '0';
x /= 10;
} while (x);
for (int i = wt; i; --i) putchar(ws[i]);
}
namespace steven24 {
int n, m, k;
int ans;
void main() {
n = read(), m = read(), k = read();
for (int i = 1; i <= k; ++i) {
int w = n - 4 * (i - 1);
int h = m - 4 * (i - 1);
ans += (w + h - 2) * 2;
}
write(ans), putchar('\n');
}
}
int main() {
steven24::main();
return 0;
}
B. Curiosity Has No Limits
denote 表示
题目大意
给定两个长为 \(n - 1(2 \le n \le 10^5)\) 的序列 \(a\) 和 \(b\)
构造一个长为 \(n\) 的序列 \(t\) 使得 \(a_i = t_i \text{ | } t_{i + 1}, b_i = t_i \text{ & } t_{i + 1}\)
如无合法方案 输出 'NO'
\(0 \le a_i, b_i, t_i \le 3\)
Solution
解法一:
假设 \(0 \le a_i, b_i, t_i \le 1\) 直接暴力分讨 \(a_i, b_i, t_i\) 的八种情况以及对应的 \(t_{i + 1}\) 的值
- \(t_i = 0, a_i = 0, b_i = 0\) 有 \(t_{i + 1} = 0\)
- \(t_i = 1, a_i = 0, a_i = 0\) 无解
- \(t_i = 0, a_i = 0, b_i = 1\) 无解
- \(t_i = 1, a_i = 0, b_i = 1\) 无解
- \(t_i = 0, a_i = 1, b_i = 0\) 有 \(t_{i + 1} = 1\)
- \(t_i = 1, a_i = 1, b_i = 0\) 有 \(t_{i + 1} = 0\)
- \(t_i = 0, a_i = 1, b_i = 1\) 无解
- \(t_i = 1, a_i = 1, b_i = 1\) 有 \(t_{i + 1} = 1\)
发现如果确定了 \(t_1\) 的值 那么整个 \(t\) 数组都唯一确定
推广到 \(0 \le a_i, b_i, t_i \le 3\) 的情况 我们枚举 \(t_1\) 的值 然后对每一位做这个即可
解法二:
有这样一个规律:\(t_i \text{ | } t_{i + 1} + t_i \text{ & } t_{i + 1} = t_i + t_{i + 1}\)
所以枚举 \(t_1\) 的值直接做即可
但是此时要注意 通过递推式 \(t_i = a_{i - 1} + b_{i - 1} - t_i\) 得到 \(t_i\) 在其中 \(a_i\) 与 \(b_i\) 的地位是等价的(即可以互换) 但是我们要钦定 \(a_i\) 为并 \(b_i\) 为交 所以我们还要判一下 \(t_i \text{ | } t_{i - 1} = a_{i - 1}\)
点击查看代码
/*
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
int xr = 0, F = 1;
char cr;
while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
while (cr >= '0' && cr <= '9')
xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
return xr * F;
}
void write(ll x) {
char ws[51];
int wt = 0;
if (x < 0) putchar('-'), x = -x;
do {
ws[++wt] = x % 10 + '0';
x /= 10;
} while (x);
for (int i = wt; i; --i) putchar(ws[i]);
}
namespace steven24 {
const int N = 1e5 + 0721;
int a[N], b[N], t[N];
int n;
bool check(int st) {
t[1] = st;
for (int i = 2; i <= n; ++i) {
t[i] = a[i - 1] + b[i - 1] - t[i - 1];
if (t[i] < 0 || t[i] > 3) return 0;
if ((t[i] & t[i - 1]) != b[i - 1]) return 0;
}
return 1;
}
void main() {
n = read();
for (int i = 1; i < n; ++i) a[i] = read();
for (int i = 1; i < n; ++i) b[i] = read();
for (int i = 0; i <= 3; ++i) {
if (check(i)) {
puts("YES");
for (int j = 1; j <= n; ++j) write(t[j]), putchar(' ');
return;
}
}
puts("NO");
}
}
int main() {
steven24::main();
return 0;
}
C. Cram Time
enumerate 枚举,列举
*1600的紫
题目大意
给定一个 \(a(0 \le a \le 10^9)\) 和 \(b(0 \le b \le 10^9)\)
构造一个方案 使第一天选取 \(n\) 个数 第二天选取 \(m\) 个数 满足:
- 这 \(n + m\) 个数不重复
- 第一天 \(n\) 个数之和在 \(a\) 以内 第二天 \(m\) 个数之和在 \(b\) 以内
- 使 \(n + m\) 最大
输出构造方案
Solution
注意一个问题 设 \(ans = n + m\) 时 选取的数集为 \(\left[1, ans\right]\) 时一定是最优的
考虑把其中任何一个数换成 \(ans + 1\) 一定会使要求更苛刻
所以我们首先可以通过前缀和直接求出 \(ans\)
考虑如何分配方案 设 \(\left[1, ans\right]\) 的前缀和为 \(sum\)
第一天的范围显然是 \(\left[sum - b, a\right]\)
我们直接贪心的从大到小枚举这 \(ans\) 个数 能加则加 过了 \(sum - b\) 就停止
剩下的全丢给第二天即可
点击查看代码
/*
首先用a+b求出和的上限 然后线性累加求出ans
从大到小考虑 加上它小于等于a则选 如果当前大于等于ans-b就跳出
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
int xr = 0, F = 1;
char cr;
while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
while (cr >= '0' && cr <= '9')
xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
return xr * F;
}
void write(ll x) {
char ws[51];
int wt = 0;
if (x < 0) putchar('-'), x = -x;
do {
ws[++wt] = x % 10 + '0';
x /= 10;
} while (x);
for (int i = wt; i; --i) putchar(ws[i]);
}
namespace steven24 {
const int N = 1e5 + 0721;
bool vis[N];
int a, b;
int n, m;
ll ans, maxn;
void main() {
a = read(), b = read();
maxn = a + b;
ll sum = 0;
for (int i = 1; 1; ++i) {
if (sum + i <= maxn) {
sum += i;
ans = i;
} else break;
}
ll sum1 = 0;
for (int i = ans; i; --i) {
if (sum1 >= sum - b) break;
if (sum1 + i <= a) {
vis[i] = 1;
++n;
sum1 += i;
}
}
m = ans - n;
write(n), putchar('\n');
for (int i = 1; i <= ans; ++i) if (vis[i]) write(i), putchar(' ');
putchar('\n');
write(m), putchar('\n');
for (int i = 1; i <= ans; ++i) if (!vis[i]) write(i), putchar(' ');
putchar('\n');
}
}
int main() {
steven24::main();
return 0;
}
D. Minimum path
obtain 获得
append 附加
*1900 的紫
题目大意
给定一个 \(n \times n (1 \le n \le 2000)\) 的字符数组 并给定一个 \(k(0 \le k \le n^2)\) 表示你可以更改这个字符数组中的 \(k\) 个元素
询问从 \((1, 1)\) 出发走到 \((n, n)\) 只能向右或向下走 路径上构成的字典序最小的字符串是什么
Solution
神似之前有套模拟赛的 T1
考虑修改操作 显然把一段前缀非'a'全改成'a'是最优的 所以我们直接设 \(f_{i, j}\) 表示走到 \((i, j)\) 并且把路径上非'a'全改成'a'需要更改几次
那么就有
- \(f_{i, j} = \min(f_{i - 1, j} + f_{i, j - 1}), mp_{i, j} = 'a'\)
- \(f_{i, j} = \min(f_{i - 1, j} + f_{i, j - 1}),otherwise\)
然后我们把所有 \(f_{i, j} \le k\) 且 \(i + j\) 最大的格子推入队列
进行 bfs 每次将能扩展到的字符最小的若干个格子都打上标记 然后推入队列
统计答案时 直接从 \((n, n)\) 倒着走 不断地走有标记的格子即可

浙公网安备 33010602011771号