Codeforces Round 895 (Div. 3) 补题报告

文章讲述了作者在编程竞赛中的经历,包括解题过程、关键题目的思路解析和AC代码示例,以及对比赛策略和基础知识不足的反思。涉及的题目类型有数学计算、逻辑思维和数据结构应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 比赛报告

共7题, 第1题AC, 第2题AC, 第3题WA, 第4题CE, 5-7题没做
本篇题解只提供前5道题

2. 比赛过程

第1题 直接AC
第2题 直接AC
第3题 考虑复杂了, 编的代码太乱, 直接跳过
第4题 还是考虑太复杂了, 编译时没注意到错误

3. 题解

3.1 A. Two Vessels

3.1.1 题目大意:

有两个容器里面分别有 a a a, b b b克水, 还有个杯子为空, 最多能装 c c c克水
可以用杯子从任意一个容器舀出小于等与 c c c克水, 倒入另一个容器里
要使两个容器水量相等, 最少需要几次移动

3.1.2 当时思路:

使 x = ( max ⁡ ( a , b ) − min ⁡ ( a , b ) ) / 2 x = (\max(a, b) - \min(a, b)) / 2 x=(max(a,b)min(a,b))/2
如果 x % c = = 0 x \% c == 0 x%c==0, a n s = x / c ans = x / c ans=x/c
否则 a n s = x / c + 1 ans = x / c + 1 ans=x/c+1

3.1.3 题目解析:

a a a b b b的差是 a b s ( a − b ) abs(a - b) abs(ab)
输出 c e i l ( a b s ( a − b ) / 2.0 − / c ) ceil(abs(a-b) / 2.0- / c) ceil(abs(ab)/2.0/c)即可

3.1.4 AC代码:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int main(){
	int t;
	int a, b, c;
	scanf("%d", &t);
	while(t--){
		cin>>a>>b>>c;
		cout << ceil(abs(a-b)/2.0/c) << '\n';
	}
	return 0;
}

3.2 The Corridor or There and Back Again

3.2.1 题目大意:

一条走廊有无数个房间, 每穿越一个房间需要1秒, 有 n n n个陷阱, 第 i i i个陷阱在 d i d_i di房间里, 会在进入房间 s i s_i si秒后触发, 不可穿过已触发陷阱的房间, 需要从第一个房间到第 k k k个房间再返回, 问最大的 k k k是多少

sdf
例: 当 n = 1 , d 1 = 2 , s i = 2 n = 1, d_1 = 2, s_i = 2 n=1,d1=2,si=2时, 当到达第 3 3 3个房间又返回第 2 2 2个房间时陷阱触发, 无法到达第 1 1 1个房间, 所以 k = 2 k = 2 k=2

3.2.2 当时思路:

1 1 1 n n n, 判断最远能走到哪一个房间, 取最小值即可, 因为到达陷阱正好触发时也不能穿过, 所以 s i s_i si − 1 -1 1, a n s = max ⁡ ( a n s , d i + ( s i − 1 ) / 2 ) ans = \max(ans, d_i + (s_i-1) / 2) ans=max(ans,di+(si1)/2)

Q: 要求最远到达距离, 为什么要取 min ⁡ \min min呢?
A: 前面的 d i + ( s i − 1 ) / 2 d_i + (s_i-1) / 2 di+(si1)/2求的就是在第 i i i个陷阱的限制下, 最远能走到的地方, 已经是最大值了, 求 min ⁡ \min min是为了满足所有陷阱的要求

3.2.3 题目解析:

同 3.2.2当时思路

3.2.4 AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int  N = 105;
int t, n, d, s, k;
int main(){
	scanf("%d", &t);
	while(t--){
		k = 1000;
		scanf("%d", &n);
		for(int i = 1;i <= n;i++){
			scanf("%d %d", &d, &s);
			k = min(k, d + (s - 1) / 2);
		}
		printf("%d\n", k);
	}
	return 0;
}

3.3 Non-coprime Split

3.3.1 题目大意:

有2个整数 l ≤ r l \le r lr, 需要找到两个整数 a a a, b b b, 满足

  • l ≤ a + b ≤ r l \le a + b \le r la+br
  • gcd ⁡ ( a , b ) ≠ 1 \gcd(a, b) \ne 1 gcd(a,b)=1

3.3.2 当时思路:

用欧拉筛出 n n n以内的素数 p i p_i pi, 便利 x : i ∼ n / 2 x:i\sim n/2 x:in/2, y = n − x y = n - x y=nx
遍历 p p p数组, 如果 x   %   p i = = 0   & &   y   %   p i = = 0 x ~ \% ~ p_i == 0 ~ \&\& ~ y ~ \% ~ p_i == 0 x % pi==0 && y % pi==0, 输出 x x x y y y, 结果TLE

3.3.3 题目解析:

定义函数 p r i m e ( x ) prime(x) prime(x)返回 x x x的最小质因子, 素数返回 0 0 0
遍历 i : l ∼ r i: l \sim r i:lr, 定义 x = p r i m e ( i ) x = prime(i) x=prime(i), 如果 x ≠ 0 x \ne 0 x=0, 输出 x x x i − x i-x ix
如果 i i i遍历完还没有输出, 那么输出 − 1 -1 1

3.3.4 AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
int t, l, r;
int prime(int x){
	for(int i = 2;i*i <= x;i++)
		if(x % i == 0)	return i;
	return 0;
}
int main(){
	scanf("%d", &t);
	while(t--){
		bool f = 0;
		scanf("%d %d", &l, &r);
		for(int i = l;i <= r;i++){
			int x = prime(i);
			if(x != 0){
				printf("%d %d\n", x, i - x);
				f = 1;
				break;
			}
		}
		if(!f)	printf("-1\n");
	}
	return 0;
}

3.4 Plus Minus Permutation

3.4.1 题目大意:

输入三个整数 n n n, x x x, y y y, 对于排列 p p p, 求 ∑ i = 1 ⌊ n x ⌋ max ⁡ ( p [ i ⋅ x ] ) − ∑ i = 1 ⌊ n y ⌋ max ⁡ ( p [ i ⋅ y ] ) \sum_{i = 1}^{\lfloor \frac{n}{x} \rfloor}{\max(p[i \cdot x])} - \sum_{i = 1}^{\lfloor \frac{n}{y} \rfloor}{\max(p[i \cdot y])} i=1xnmax(p[ix])i=1ynmax(p[iy]), 也就是所有 i i i能被 x x x整除的 p i p_i pi之和减去所有 i i i能被 y y y整除的 p i p_i pi之和的最大值

3.4.2 当时思路:

用欧拉筛出素数, 求出 x x x y y y共有的素数积为 z z z, 求 n n n往前数 x − z x-z xz个数的和 − - 1 1 1向后数 y − z y-z yz个数的和

3.4.3 题目解析:

让答案尽可能大, 就是让前一项尽可能大, 后一项尽可能小, 也就是 x x x倍数位置的数尽可能大, y y y背书位置上的数尽可能小, l c m ( x , y ) lcm(x, y) lcm(x,y)位置上的数又被加又被减, 就不用管
z = l c m ( x , y ) z = lcm(x, y) z=lcm(x,y), 求 n n n往前数 x − z x-z xz个数的和 − - 1 1 1向后数 y − z y-z yz个数的和

3.4.4 AC代码:

#include <iostream>
#include <cstdio>
typedef long long ll;
using namespace std;
ll t, n, x, y, z, sumx, sumy;

ll lcm(ll, ll);
ll gcd(ll, ll);
ll lcm(ll a, ll b){
	return a / gcd(a, b) * b;
}
ll gcd(ll a, ll b){
	if(a % b == 0)	return b;
	return gcd(b, a % b);
}

int main(){
	scanf("%lld", &t);
	while(t--){
		scanf("%lld %lld %lld", &n, &x, &y);
		z = n / lcm(x, y);
		x = n / x - z;
		y = n / y - z;
		sumx = (n-x+1 + n) * x / 2;
		sumy = (1 + y) * y / 2;
		printf("%lld\n", sumx - sumy);
	}
	return 0;
}

3.5 Data Structures Fan

3.5.1 题目大意:

n n n个整数 a 1 a_1 a1, a 2 ⋯ a n a_2 \cdots a_n a2an, 和一个长度为 n n n的二进制字符串(只包含 0 0 0 1 1 1的字符串)
q q q次查询, 有两种类型:

  • 1 l r 把 s l s_l sl s r s_r sr取反
  • 2 g 计算所有 s i = g s_i = g si=g下标 i i i的数字 a i a_i ai的异或和

3.5.2 当时思路:

暴力

3.5.3 题目解析:

要知道 0 ⊕ n = n 0 \oplus n = n 0n=n, n ⊕ n = 0 n \oplus n = 0 nn=0
x 0 x_0 x0等于所有 s i = 0 s_i = 0 si=0 a i a_i ai异或和, 设 x 1 x_1 x1等于所有 s i = 1 s_i = 1 si=1 a i a_i ai异或和
对于操作1, 设 x 0 = a 1 ⊕ a 2 ⊕ a 3 ⊕ a 4 x_0 = a_1 \oplus a_2 \oplus a_3 \oplus a_4 x0=a1a2a3a4, 现在 s 1 s_1 s1 s 3 s_3 s3被取反, 这时我们需要 x 0 = x 0 ⊕ a 1 ⊕ a 2 ⊕ a 3 x_0 = x_0 \oplus a_1 \oplus a_2 \oplus a_3 x0=x0a1a2a3, 把原来的 a 1 a_1 a1 a 3 a_3 a3抵消掉, 这样 x 0 = a 4 x_0 = a_4 x0=a4, 需要用前缀和优化
对于操作2, 直接输出 x 0 x_0 x0 x 1 x_1 x1

3.5.4 代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5 + 5;
int n, q, t, c, a[N], tp, l, r, g;
int main(){
	scanf("%d", &t);
	while(t--){
		int sum[N] = {0}, x0 = 0, x1 = 0, x;
		scanf("%d", &n);
		for(int i = 1;i <= n;i++){
			scanf("%d", &a[i]);
			sum[i] = sum[i-1] ^ a[i];
		}
		for(int i = 1;i <= n;i++){
			scanf("%c", &c);
			if(c == '0')	x0 ^= a[i];
			else	x1 ^= a[i];
		}
		scanf("%d", &q);
		while(q--){
			scanf("%d", &tp);
			if(tp == 1){
				scanf("%d %d", &l, &r);
				x = sum[l-1] ^ sum[r];
				x0 ^= x;
				x1 ^= x;
			}
			else{
				scanf("%d", &g);
				if(g == 0)	printf("%d ", x0);
				else	printf("%d ", x1);
			}
		}
		printf("\n");
	}
	return 0;
}

4. 反思

4.1 回顾问题

思路不够好, 基础知识不太巩固。不到最后一刻就不能放弃

4.2 如何改进

多刷题

本文为可达鸭Y1第4课补题报告
感谢观看
2023.10.14 Sat.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值