Technocup 2019 - Elimination Round 2 解题报告

对应场次为 CF1031


A. Golden Plate

gild 镀金

题目大意

给定一个 n×m(1n,m100) 的矩形 你需要铺 k 圈黄金地砖
其中最外面那圈为第一层 然后隔一层再铺一圈为第二层 就像这样
image
显然 第 i 层地砖的长宽分别为 n4×(i1),m4×(i1) (题干给的)
你需要计算一共要铺多少个地砖
保证 k 圈地砖都能铺上

Solution

通过观察不难发现 一个 n×m 的矩形外层有 (n+m2)×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 表示

题目大意

给定两个长为 n1(2n105) 的序列 ab
构造一个长为 n 的序列 t 使得 ai=ti | ti+1,bi=ti & ti+1
如无合法方案 输出 'NO'
0ai,bi,ti3

Solution

解法一:
假设 0ai,bi,ti1 直接暴力分讨 ai,bi,ti 的八种情况以及对应的 ti+1 的值

  • ti=0,ai=0,bi=0ti+1=0
  • ti=1,ai=0,ai=0 无解
  • ti=0,ai=0,bi=1 无解
  • ti=1,ai=0,bi=1 无解
  • ti=0,ai=1,bi=0ti+1=1
  • ti=1,ai=1,bi=0ti+1=0
  • ti=0,ai=1,bi=1 无解
  • ti=1,ai=1,bi=1ti+1=1
    发现如果确定了 t1 的值 那么整个 t 数组都唯一确定
    推广到 0ai,bi,ti3 的情况 我们枚举 t1 的值 然后对每一位做这个即可

解法二:
有这样一个规律:ti | ti+1+ti & ti+1=ti+ti+1
所以枚举 t1 的值直接做即可
但是此时要注意 通过递推式 ti=ai1+bi1ti 得到 ti 在其中 aibi 的地位是等价的(即可以互换) 但是我们要钦定 ai 为并 bi 为交 所以我们还要判一下 ti | ti1=ai1

点击查看代码
/*
*/
#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(0a109)b(0b109)
构造一个方案 使第一天选取 n 个数 第二天选取 m 个数 满足:

  • n+m 个数不重复
  • 第一天 n 个数之和在 a 以内 第二天 m 个数之和在 b 以内
  • 使 n+m 最大
    输出构造方案

Solution

注意一个问题 设 ans=n+m 时 选取的数集为 [1,ans] 时一定是最优的
考虑把其中任何一个数换成 ans+1 一定会使要求更苛刻
所以我们首先可以通过前缀和直接求出 ans

考虑如何分配方案 设 [1,ans] 的前缀和为 sum
第一天的范围显然是 [sumb,a]
我们直接贪心的从大到小枚举这 ans 个数 能加则加 过了 sumb 就停止
剩下的全丢给第二天即可

点击查看代码
/*
首先用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×n(1n2000) 的字符数组 并给定一个 k(0kn2) 表示你可以更改这个字符数组中的 k 个元素
询问从 (1,1) 出发走到 (n,n) 只能向右或向下走 路径上构成的字典序最小的字符串是什么

Solution

神似之前有套模拟赛的 T1
考虑修改操作 显然把一段前缀非'a'全改成'a'是最优的 所以我们直接设 fi,j 表示走到 (i,j) 并且把路径上非'a'全改成'a'需要更改几次
那么就有

  • fi,j=min(fi1,j+fi,j1),mpi,j=a
  • fi,j=min(fi1,j+fi,j1),otherwise

然后我们把所有 fi,jki+j 最大的格子推入队列
进行 bfs 每次将能扩展到的字符最小的若干个格子都打上标记 然后推入队列
统计答案时 直接从 (n,n) 倒着走 不断地走有标记的格子即可

posted @   Steven24  阅读(492)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示