0%

codeforces 2091G 题解

前言

也是之前赛时没做出来的一道题,题意很简单,做法也不难,但是计算时间复杂度可能会难一些,所以很多看起来比较暴力的写法不感谢


题意

对于[0, s]的一段区间,第一步可以走任意次k格(走到k、2k、3k……),第二次需要掉头,走任意次(k-1)格,第三次掉头走任意次(k-2)格……第k次掉头走任意次1格,之后走的距离固定为每次1格,求最早到达s的时间。

思路

先找到一些特例:

  1. s % k == 0:直接输出k即可
  2. k < 2,那么最终答案一定为1
  3. k * k <= s,最终答案一定为k或k-2,取决于k是否整除s

前两个特判比较显然,这题的核心在于第三个特判:

如果能量为k时到达s,那么s一定被k整除,否则无法到达s;

如果能量为k-1,Gleb的方向指向起点,不可能在此时到达s;

当能量为k-2时,能否到达s与前两次的步数有关。但注意到我们在第一步走k次,我们在第二次操作时通过左移某个步数x一定可以实现$s - k \times k + x \times (k - 1) \mod (k - 2) = 0$

证毕.

进行完特判之后,我们开始考虑对其余情况的判断。假设答案为ans,我们如果在每次遍历从0到s所有的位置,总的时间复杂度为$O(s\times(k - ans))$,这个时间复杂度能否被接受取决于s和ans的范围

根据特判3,如果s过大,超过k方,我们可以直接将其特判掉,不需要考虑其1e9的范围;

对于ans,我们同样可以根据特判三估计出其可能的最小值应该接近$\sqrt s$.

所以设最大运行时间为T,T可以近似为$s \times (k - \sqrt s)$

T什么时候取最大值呢?

考虑均值不等式,

T取得最大值$\frac{4k^3}{27}$

这个复杂度是勉强可以接受的,只是需要注意下常数。

至此,我们对步数从k到1遍历,可以使用bfs进行更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <bits/stdc++.h>

using namespace std;

int s, k;
void solve() {
cin >> s >> k;
if(s % k == 0) {
cout << k << endl;
return;
}

if(k <= 2) {
cout << 1 << endl;
return;
}

if(s >= k * k) {
cout << k - 2 << endl;
return;
}

queue<pair<int, int>> q;
q.push({0, k + 1});
for(int i = k; i >= 1; --i) {
if(i % 2 == k % 2) {
set<int> st;
while(q.front().second > i) {
int top = q.front().first; q.pop();
if(st.count(top)) continue;
for(int j = top + i; j <= s and !st.count(j); j += i) {
st.insert(j);
q.push({j, i});
}
}
if(st.count(s)) {
cout << i << endl;
return;
}
} else {
set<int> st;
while(q.front().second > i) {
int top = q.front().first; q.pop();
if(st.count(top)) continue;
for(int j = top - i; j >= 0 and !st.count(j); j -= i) {
st.insert(j);
q.push({j, i});
}
}
if(st.count(s)) {
cout << i << endl;
return;
}
}
}
cout << 1 << endl;
return;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while(t--) {
solve();
}

return 0;
}

后记

题目本身不难,但是如果赛时因为常数TLE掉怀疑自己时间复杂度算错也不是不可能……

我很可爱,请给我钱qwq