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(a−b)
输出
c
e
i
l
(
a
b
s
(
a
−
b
)
/
2.0
−
/
c
)
ceil(abs(a-b) / 2.0- / c)
ceil(abs(a−b)/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是多少
例: 当
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+(si−1)/2)
Q: 要求最远到达距离, 为什么要取 min \min min呢?
A: 前面的 d i + ( s i − 1 ) / 2 d_i + (s_i-1) / 2 di+(si−1)/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 l≤r, 需要找到两个整数 a a a, b b b, 满足
- l ≤ a + b ≤ r l \le a + b \le r l≤a+b≤r
- 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:i∼n/2,
y
=
n
−
x
y = n - x
y=n−x
遍历
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:l∼r, 定义
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
i−x
如果
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=1⌊xn⌋max(p[i⋅x])−∑i=1⌊yn⌋max(p[i⋅y]), 也就是所有 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 x−z个数的和 − - − 1 1 1向后数 y − z y-z y−z个数的和
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
x−z个数的和
−
-
−
1
1
1向后数
y
−
z
y-z
y−z个数的和
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
a2⋯an, 和一个长度为
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
0⊕n=n,
n
⊕
n
=
0
n \oplus n = 0
n⊕n=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=a1⊕a2⊕a3⊕a4, 现在
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=x0⊕a1⊕a2⊕a3, 把原来的
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.