Technocup 2019 - Elimination Round 2 解题报告

对应场次为 CF1031


A. Golden Plate

gild 镀金

题目大意

给定一个 \(n \times m (1 \le n, m \le 100)\) 的矩形 你需要铺 \(k\) 圈黄金地砖
其中最外面那圈为第一层 然后隔一层再铺一圈为第二层 就像这样
image
显然 第 \(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)\) 倒着走 不断地走有标记的格子即可

posted @ 2023-10-25 18:03  Steven24  阅读(617)  评论(0)    收藏  举报