CF系列题解
Educational Codeforces Round 125 (Rated for Div. 2)
题目
A. Integer Moves
原题链接
题意
给定一个整数对 ( x , y ) (x, y) (x,y) ,求从 ( 0 , 0 ) (0, 0) (0,0) 转移过来需要几步,每次转移的直线距离必须是整数,即 ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 \sqrt{(x_1−x_2)^2+(y_1−y_2)^2} (x1−x2)2+(y1−y2)2 为 整数 。
输入格式
第一行包含一个整数
t
(
1
≤
t
≤
3000
)
t (1≤t≤3000)
t(1≤t≤3000) ,表示有t组测试数据。
每个测试数据第一行包含两个整数
x
,
y
(
0
≤
x
,
y
≤
50
)
x, y (0≤x,y≤50)
x,y(0≤x,y≤50) 。
输出格式
输出操作次数。
输入样例:
3
8 6
0 0
9 15
输出样例:
1
0
2
题解
思路
分类讨论即可:
- 若 ( x , y ) = ( 0 , 0 ) (x, y) = (0, 0) (x,y)=(0,0) ,不需要操作,输出 0 0 0。
- 若 x 2 + y 2 \sqrt{x^2+y^2} x2+y2 为整数,只需要操作一次即可,输出 1 1 1。
- 否则我们可以先走到 ( x , 0 ) (x, 0) (x,0),再走到 ( x , y ) (x, y) (x,y),只需要操作两次,输出 2 2 2 。
关键在于如何判断条件2,实际上也不难。
注意该题会爆 int 。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
void solve()
{
int x, y;
cin >> x >> y;
if (x == 0 && y == 0) {
cout << "0\n";
} else {
int d = sqrt(x * x + y * y); // 条件二的判断
if (d * d == x * x + y * y) {
cout << "1\n";
} else {
cout << "2\n";
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
B. XY Sequence
原题链接
题意
给定四个数 n , B , x , y n , B, x, y n,B,x,y ,构造一个序列 a 0 , a 1 , a 2 , … , a n a_0,a_1,a_2,…,a_n a0,a1,a2,…,an ,其中 a 0 = 0 a_0=0 a0=0 ,对于任意 i ≥ 1 i≥1 i≥1 满足:
- a i ≤ B a_i≤B ai≤B
- a i = a i − 1 + x a_i=a_{i−1}+x ai=ai−1+x
- a i = a i − 1 − y a_i=a_{i−1}-y ai=ai−1−y
使得 ∑ i = 0 n a i \sum_{i=0}^na_i ∑i=0nai 的值最大。
输入格式
第一行包含一个整数
t
(
1
≤
t
≤
1
0
4
)
t (1≤t≤10^4)
t(1≤t≤104) ,表示有t组测试数据。
每个测试数据第一行包含四个整数
n
,
B
,
x
,
y
(
1
≤
n
≤
2
⋅
1
0
5
;
1
≤
B
,
x
,
y
≤
1
0
9
)
n, B, x , y (1≤n≤2⋅10^5; 1≤B,x,y≤10^9)
n,B,x,y(1≤n≤2⋅105;1≤B,x,y≤109) 。
n
n
n 的总和不会超过
2
⋅
1
0
5
2⋅10^5
2⋅105 。
输出格式
输出最大的
∑
i
=
0
n
a
i
\sum_{i=0}^na_i
∑i=0nai 。
输入样例:
3
5 100 1 30
7 1000000000 1000000000 1000000000
4 1 7 3
输出样例:
15
4000000000
-10
题解
思路
因为要使得和最大,因此应该每一个元素都尽可能的大,所以当
a
i
−
1
+
x
≤
B
a_{i−1}+x≤B
ai−1+x≤B 时
a
i
=
a
i
−
1
+
x
a_i=a_{i−1}+x
ai=ai−1+x ,否则
a
i
=
a
i
−
1
−
y
a_i=a_{i−1}-y
ai=ai−1−y 。
当然这一题也会爆 int 。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
void solve()
{
int n, B, x, y;
cin >> n >> B >> x >> y;
int ans = 0;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++ ) {
if (a[i - 1] + x <= B) {
a[i] = a[i - 1] + x;
} else {
a[i] = a[i - 1] - y;
}
ans += a[i];
}
cout << ans << "\n";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
C. Bracket Sequence Deletion
原题链接
题意
给定一个由
(
(
( 和
)
)
) 组成的长度为
n
n
n 的字符串,你可以进行若干次操作——删去最短的 好 的前缀。
一个前缀被称为 好 需要满足以下条件:
- 前缀是 常规括号序列(即一个左括号对应一个右括号)。
- 前缀是至少是长度为 2 2 2 的 回文 的。
例子:(())()
,()
,(()(()))
是 常规括号序列,)(
,(()
,(()))(
不是常规括号序列。 ))
,((
,)(()
是 回文序列 ,()
, )(
,))(
不是回文序列。
注:感觉这个回文有点恶心,读题读了半小时呜呜呜。
输入格式
第一行包含一个整数
t
(
1
≤
t
≤
1
0
4
)
t (1≤t≤10^4)
t(1≤t≤104) ,表示有t组测试数据。
每个测试数据第一行包含一个整数
n
(
1
≤
n
≤
2
⋅
1
0
5
)
n(1≤n≤2⋅10^5)
n(1≤n≤2⋅105) ,表示序列长度。
每个测试数据第二行包含一个字符串
s
s
s 。
n
n
n 的总和不会超过
2
⋅
1
0
5
2⋅10^5
2⋅105 。
输出格式
每个测试数据输出两个整数
c
c
c 和
r
r
r ,表示操作次数和剩余字符串的长度。
输入样例:
5
2
()
3
())
4
((((
5
)((()
6
)((()(
输出样例:
1 0
1 1
2 0
1 0
1 1
题解
思路
模拟题,关键在于每次删除的是最短的好的前缀。
- 假设此时
s[0] == '('
,无论是和s[1] == ')'
还是s[1] == '('
均可组成一个好的前缀,因此可以直接删除。(原因:题目要求删除 最短 的好的前缀)。 - 否则,此时前缀无法满足体中所给的限制1,即前缀一定不可能是常规括号序列,因此只能和后面的部分构成回文。构成回文我们只需要找到后面第一个
s[j] == s[0]
即可,这部分的实现用双指针随便搞搞就行了。倘若我们找不到,意味着此时的前缀我们已经删不掉了,直接终止循环即可。
只需要遍历一遍字符串,时间复杂度为
O
(
n
)
O(n)
O(n) 。
细节详见代码(比赛写的很丑,将就一下)。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
void solve()
{
int n;
cin >> n;
string s;
cin >> s;
int ans = 0; // 记录操作次数
int last = -1; // last用来记录终止循环时我们删到哪里了
for (int i = 0; i < n; i ++ ) {
if (s[i] == '(' && i < n - 1) { // 第一种情况
ans ++ ;
i ++ ;
last = i; // 更新last
continue;
} else {
bool flag = false; // 用flag记录我们是否找到s[j] == s[i]
for (int j = i + 1; j < n; j ++ ) {
if (s[j] == s[i]) {
flag = true;
ans ++ ;
i = j;
last = i;
break;
}
}
if (flag) continue;
else break; // 找不到的话直接break就行了
}
break;
}
cout << ans << " " << n - last - 1 << "\n";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
D. For Gamers. By Gamers
原题链接
题意
有
n
n
n 种士兵和
C
C
C 个硬币,每种士兵有三个属性:招募一个所需要的硬币数
c
i
c_i
ci ,生命值
d
i
d_i
di 和攻击力
h
i
h_i
hi 。给出
m
m
m 个询问,每个询问包含一个攻击力为
D
j
D_j
Dj 生命值为
H
j
H_j
Hj 的魔王,问击败魔王至少需要花多少钱。
有以下限制:
- 每次招募只能选择一种士兵。
- 士兵在战斗中不能死亡。
- 魔王与士兵的战斗是连续的,即双方会在同一时间攻击。(这是一个简化条件)
输入格式
第一行包含两个整数
n
,
C
(
1
≤
n
≤
3
⋅
1
0
5
;
1
≤
C
≤
1
0
6
)
n,C (1≤n≤3⋅10^5; 1≤C≤10^6)
n,C(1≤n≤3⋅105;1≤C≤106) ,含义见题面。
接下来
n
n
n 行每行包含三个整数
c
i
,
d
i
,
h
i
(
1
≤
c
i
≤
C
;
1
≤
d
i
,
h
i
≤
1
0
6
)
c_i,d_i,h_i (1≤c_i≤C; 1≤d_i,h_i≤10^6)
ci,di,hi(1≤ci≤C;1≤di,hi≤106) 。
下面一行包含一个整数
m
(
1
≤
m
≤
3
⋅
1
0
5
)
m (1≤m≤3⋅10^5)
m(1≤m≤3⋅105) 。
接下来
m
m
m 行每行包含两个整数
D
j
,
H
j
(
1
≤
D
j
≤
1
0
6
;
1
≤
H
j
≤
1
0
12
)
Dj, Hj (1≤D_j≤10^6; 1≤H_j≤10^{12})
Dj,Hj(1≤Dj≤106;1≤Hj≤1012),表示询问
输出格式
输出
m
m
m 个整数,表示击败魔王的最小代价,若不能击败,输出
−
1
-1
−1 。
输入样例:
3 10
3 4 6
5 5 5
10 3 4
3
8 3
5 4
10 15
输出样例:
5 3 -1
输入样例:
5 15
14 10 3
9 2 2
10 4 3
7 3 5
4 3 1
6
11 2
1 1
4 7
2 1
1 14
3 3
输出样例:
14 4 14 4 7 7
输入样例:
5 13
13 1 9
6 4 5
12 18 4
9 13 2
5 4 5
2
16 3
6 2
输出样例:
12 5
题解
思路
切记读题要认真。
调和级数复杂度(比赛时群里偷的题解,但也没想出来,呜呜呜太菜了)。
由数据范围得显然要预处理。
由于 条件3 的限制,我们可以不用考虑先后手的问题,同时由于 条件2 的限制,招募多个士兵本质上就是攻击力的叠加,对于生命值没有影响,因此当士兵可以击败魔王时应满足 H / d < h / D H / d < h / D H/d<h/D(大写字母表示魔王,小写字母表示士兵),移项得 H × D < h × d H × D < h × d H×D<h×d ,因此可以得把两个属性合并为一个属性。
有点像这个题:AcWing 125. 耍杂技的牛
接下来就是预处理,我们用一个长度为 C + 1 C+1 C+1 的数组 m a x v a l [ C + 1 ] maxval[C+1] maxval[C+1] 记录当花费为 i i i 时可以达到的最高属性值为 m a x v a l [ i ] maxval[i] maxval[i] 。预处理复杂度为 O ( c + c / 2 + c / 3 + . . . + c / c ) = O ( c l o g c ) O(c+c/2+c/3+...+c/c)=O(clogc) O(c+c/2+c/3+...+c/c)=O(clogc) ,调和级数呜呜呜。
预处理好后对于每个询问直接二分即可,注意这里是严格大于,所以要用 upper_bound
。(STL YYDS)
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> maxval(m + 1);
for (int i = 0; i < n; i ++ ) {
int c, d, h;
cin >> c >> d >> h;
maxval[c] = max(maxval[c], d * h);
}
for (int i = 1; i <= m; i ++ ) {
for (int j = 2 * i; j <= m; j += i) {
maxval[j] = max(maxval[j], maxval[i] * (j / i));
}
}
for (int i = 1; i <= m; i ++ ) {
maxval[i] = max(maxval[i], maxval[i - 1]);
}
int q;
cin >> q;
while (q -- ) {
int d, h;
cin >> d >> h;
int t = upper_bound(maxval.begin(), maxval.end(), d * h) - maxval.begin();
cout << (t > m ? -1 : t) << " ";
}
return 0;
}