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

本文分享了IT竞赛中的五道题目及其解法,包括计数出现次数最多的元素、构造递增数组、和的计算、字符串翻转和位运算查询。解题策略和AC代码展示了编程技巧的应用。

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

题目链接

1. 比赛报告

共7题, 前3题AC, 第4题TLE, 第5题WA, 6-7题没做
本题解暂提供前5道题

2. 比赛过程

前3题 直接AC
第4,5题 暴力

3. 题解

3.1 A. How Much Does Daytona Cost?

3.1.1 题目大意:

给定有 n n n个整数组成的数组 a a a, 求是否有一个 a a a的字段, k k k在其中出现次数最多

3.1.2 当时思路:

如果 a x = k a_x = k ax=k, 就有一个字段 [ a x , a x ] [a_x, a_x] [ax,ax] k k k出现次数最多, 答案是YES
否则即 a a a中没有 k k k这个元素, 答案是NO

3.1.3 题目解析:

3.1.2

3.1.4 AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
int main(){
	int t, n, k;
	scanf("%d", &t);
	while(t--){
		bool f = 0;
		int a;
		scanf("%d %d", &n, &k);
		while(n--){
			scanf("%d", &a);
			if(a == k)	f = 1;
		}
		if(f)	printf("YES\n");
		else	printf("NO\n");
	}
	return 0;
}

3.2 B. Aleksa and Stack

3.2.1 题目大意:

构造一个长度为 n n n严格递增的正整数数组, 要求每个 1 ≤ i ≤ n − 2 1 \le i \le n-2 1in2, a i + a i + 1 ∤ a i + 2 a_i+a_{i+1}\nmid a_{i+2} ai+ai+1ai+2

3.2.2 当时思路:

预处理从 1 1 1开始遍历, 如果符合要求加入 a n s ans ans数组中
每次输出数组前 n n n个数

3.2.3 题目解析:

奇数 + + +奇数 = = =偶数, 偶数 ∤ \nmid 奇数, 输出前 n n n个奇数

3.2.4 当时AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 2e5;
int t, n, a[N + 5];
int main(){
	a[1] = 1;
	a[2] = 3;
	for(int i = 3, cnt = 4;i <= N;i++, cnt++){
		while((3 * cnt) % (a[i-2] + a[i-1]) == 0)	cnt++;
		a[i] = cnt;
	}
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		for(int i = 1;i <= n;i++)
			printf("%d ", a[i]);
		printf("\n");
	}
	return 0;
}

至于AC代码, 你肯定会输出前 2 × 1 0 5 2\times10^5 2×105个整数

3.3 Vasilije in Cacak

3.3.1 题目大意:

给你 3 3 3个整数 n n n, k k k, x x x, 从 1 1 1 n n n中选 k k k个整数, 和是否能等于 x x x

3.3.2 当时思路:

举个例子: n = 10 , k = 3 n=10, k=3 n=10,k=3

  1. x = 1 x = 1 x=1, 答案是NO, 因为最小和是 1 + 2 + 3 = 6 1+2+3 = 6 1+2+3=6
  2. x = 1 0 8 x = 10^8 x=108, 答案是NO, 因为最大和是 8 + 9 + 10 = 27 8+9+10 = 27 8+9+10=27

可以得到, 当 x < ∑ 1 k x < \sum_1^k x<1k或者 x > ∑ n − k + 1 n x > \sum_{n-k+1}^n x>nk+1n时答案是NO, 否则答案是YES

3.3.3 题目解析:

3.3.2

3.3.4 AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
long long t, n, k, x, y, z;
int main(){
	scanf("%lld", &t);
	while(t--){
		scanf("%lld %lld %lld", &n, &k, &x);
		y = (1 + k) * k / 2;
		z = (n-k+1 + n) * k / 2;
		if(y <= x && x <= z)	printf("YES\n");
		else	printf("NO\n");
	}
	return 0;
}

3.4 D. Reverse Madness

3.4.1 题目大意:

输入长度为 n n n的字符串 s s s和长度为 k k k的数组 l l l r r r, 满足以下条件:

  • l 1 = 1 , r k = n l_1 = 1, r_k = n l1=1,rk=n
  • l i < r i l_i < r_i li<ri
  • l i = r i − 1 + 1 l_i = r_{i-1}+1 li=ri1+1

也就是长度为 n n n的字符串 s s s被分成 k k k份, 第 i i i段的起点和终点是 l i l_i li r i r_i ri
q q q次查询, 每次查询给一个整数 1 ≤ x ≤ n 1\le x\le n 1xn, 求出所属的区间 i i i
[ min ⁡ ( x , l i + r i − x ) , max ⁡ ( x , l i + r i − x ) ] \big[\min(x, l_i+r_i-x), \max(x, l_i+r_i-x)\big] [min(x,li+rix),max(x,li+rix)]翻转

3.4.2 当时思路:

二分出 x x x所属的区间直接翻转

3.4.3 题目解析:

∵ l i ≤ x ≤ r i \because l_i \le x \le r_i lixri
∴ l i ≤ l i + r i − x ≤ r i \therefore l_i \le l_i+r_i-x \le r_i lili+rixri, 在区间内 x x x l i + r i − x l_i+r_i-x li+rix过中点相对
n n n分成 k k k个组, 每组统计反转次数 d j d_j dj, 如果 d j d_j dj是奇数, 把它与所对应的 l i + r i − j l_i+r_i-j li+rij交换
其他具体内容见代码

3.4.4 AC代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;

const int N = 2e5 + 5;
int t, n, k, q, l[N], r[N], p[N];
int x, y, d[N];
string s;
int main(){
	scanf("%d", &t);
	while(t--){
		memset(d, 0, sizeof(d));  //建立差分数组d
		
		scanf("%d %d", &n, &k);
		cin >> s;
		for(int i = 1;i <= k;i++)
			scanf("%d", &l[i]);
		for(int i = 1;i <= k;i++){
			scanf("%d", &r[i]);
			for(int j = l[i];j <= r[i];j++){
				p[j] = i;  //记录i所属的区间p
			}
		}
		scanf("%d", &q);
		while(q--){
			scanf("%d", &x);
			y = l[p[x]] + r[p[x]] - x;
			d[min(y, x)]++;
			d[max(y, x)+1]--;  //进行差分
		}
		for(int i = 1;i <= n;i++)
			d[i] += d[i-1];  //求出原来交换的次数
		for(int i = 1;i <= k;i++){
			for(int j = l[i];j <= (l[i]+r[i])/2;j++){
				if(d[j] & 1)
					swap(s[j-1], s[l[i]+r[i]-j-1]);
			}
		}
		cout << s << "\n";
	}
	return 0;
}

3.5 E. Iva & Pav

3.5.1 题目大意:

输入长度为 n n n的数组 a a a, f ( l , r ) = a l & a l + 1 & … & a r f(l, r) = a_l \& a_{l+1} \& \dots \& a_r f(l,r)=al&al+1&&ar(按位与), 有 q q q次查询, 每次查询输入 l l l k k k, 求最大的 r r r使 f ( l , r ) ≥ k f(l, r) \ge k f(l,r)k

3.5.2 当时思路:

暴力

3.5.3 题目解析:

由于左端点 l l l和右端点 n n n是固定的, 可以使用二分查找最大的 r r r
这样就需要快速求出 f ( l , r ) f(l, r) f(l,r), 本题不涉及修改操作, 还是一个可重复贡献问题, 可以使用ST表
如果不了解ST表可以参考本人的另一篇文章ST表介绍, 写的不太详细, 大家多多评论

3.5.4 AC代码:

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 2e5 + 5, M = 35;
int t, n, q, l, k;
int f[N][M], l2[N];

void pre(){
	l2[0] = -1;
	for(int i = 1;i < N;i++)
		l2[i] = l2[i/2] + 1;
}
void build(){
	for(int j = 1;(1 << j) <= n;j++)
		for(int i = 1;i + (1 << j) - 1 <= n;i++)
			f[i][j] = f[i][j-1] & f[i+(1<<j-1)][j-1];
}
int find(int l, int r){
	int k = l2[r-l+1];
	return f[l][k] & f[r-(1<<k)+1][k];
}

int main(){
	pre();
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		for(int i = 1;i <= n;i++)
			scanf("%d", &f[i][0]);
		build();
		
		scanf("%d", &q);
		while(q--){
			scanf("%d %d", &l, &k);
			if(f[l][0] < k){
				printf("-1 ");
				continue;
			}
			int L = l, R = n, r;
			while(L < R){
				r = (L + R + 1) / 2;
				if(find(l, r) < k)	R = r - 1;
				else	L = r;
			}
			printf("%d ", L);
		}
	}
}

4. 反思

4.1 回顾问题

不会把以前和现在的知识结合起来

4.2 如何改进

更改做题方式: 如果不会做翻翻笔记, 看看有没有可以用上的知识


这是我的帐号: I_AK-IOI

本文为可达鸭Y1第7课补题报告
感谢观看
Nov.11.2023 Sat.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值