|
|
|
|
### [$P9748 [CSP-J 2023]$ 小苹果](https://www.luogu.com.cn/problem/P9748?contestId=140858)
|
|
|
|
|
|
|
|
|
|
第一题,一般不会用到什么算法,函数什么的都不会用到
|
|
|
|
|
|
|
|
|
|
#### 1、看数据范围,测试点$1 \sim 2,n<=10$,此时可以打表
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
if (n == 1)
|
|
|
|
|
res = 1, day = 1;
|
|
|
|
|
else if (n == 2)
|
|
|
|
|
res = 2, day = 2;
|
|
|
|
|
else if (n == 3)
|
|
|
|
|
res = 3, day = 3;
|
|
|
|
|
else if (n == 4)
|
|
|
|
|
res = 3, day = 1;
|
|
|
|
|
else if (n == 5)
|
|
|
|
|
res = 4, day = 4;
|
|
|
|
|
else if (n == 6)
|
|
|
|
|
res = 4, day = 2;
|
|
|
|
|
else if (n == 7)
|
|
|
|
|
res = 4, day = 1;
|
|
|
|
|
else if (n == 8)
|
|
|
|
|
res = 5, day = 5;
|
|
|
|
|
else if (n == 9)
|
|
|
|
|
res = 5, day = 3;
|
|
|
|
|
else if (n == 10)
|
|
|
|
|
res = 5, day = 1;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
用人脑算一下,然后写上去,这就是打表,可以得$20$分。
|
|
|
|
|
|
|
|
|
|
#### 2、$3 \sim 5 \ n<=1000$
|
|
|
|
|
此时可以开一个$bool$数组,每次遍历$n*n=1000000$次,可以过,又得到$20$分。
|
|
|
|
|
当在把$st[n]$设置为$true$时,记录现在是第几天就行了。
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 1000010;
|
|
|
|
|
int st[N];
|
|
|
|
|
|
|
|
|
|
int cnt; // 已经取出的个数
|
|
|
|
|
int day; // 第n个苹果是第day天被取走的
|
|
|
|
|
int all; // 一共需要多少天
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
// freopen("apple.in", "r", stdin);
|
|
|
|
|
// freopen("apple.out", "w", stdout);
|
|
|
|
|
int n;
|
|
|
|
|
cin >> n;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
// 模拟每一天
|
|
|
|
|
all++;
|
|
|
|
|
|
|
|
|
|
int c = 0;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
if (st[i]) continue;
|
|
|
|
|
c++;
|
|
|
|
|
if (c % 3 == 1) { // 找规律,1,4,7,10...出列
|
|
|
|
|
st[i] = 1;
|
|
|
|
|
cnt++; // 又出队一个
|
|
|
|
|
if (i == n) day = all; // 记录n是哪天出队的
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (cnt == n) break; // 如果出队数量达标就完事
|
|
|
|
|
}
|
|
|
|
|
// 输出一共需要多少天,取出第n号在第几天
|
|
|
|
|
cout << all << " " << day << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 3、$6 \sim 7$ $10^6$的数据范围
|
|
|
|
|
|
|
|
|
|
明显刚才$O(N*N)$的做法不行,太慢了。
|
|
|
|
|
|
|
|
|
|
此数据有特殊性质:小苞第一天就取走编号为 $n$ 的苹果。
|
|
|
|
|
|
|
|
|
|
按题意假设有$81$个苹果,实际上变化的是
|
|
|
|
|
$81->81*2/3=54->54*2/3=36 ->24*2/3=16 -> 16 * 2/3 = ...$
|
|
|
|
|
|
|
|
|
|
你会发现下降的特别快,也就是一次减少$2/3$,那么$2$次就是减少$2/3*2/3=4/9$,而$4/9<1/2$,也就是两次减少$1$半
|
|
|
|
|
|
|
|
|
|
每次减少$1$半是$log_2{N}$,两次减少一半,就是$2*log_2{N}$,也就是是一个$logN$ 的时间复杂度,并不是$O(N*N)$,是$O(2NlogN)$ 也就是说,上面的代码可以得到$70$分!
|
|
|
|
|
|
|
|
|
|
#### 4、数学解法
|
|
|
|
|
|
|
|
|
|
1. 首先,以3个苹果为一组,每次会取走每一组的第一个,第一次取走苹果的编号为1、4、7.
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
2. 第一次取完苹果后,剩余苹果总数和编号如下:
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
3. 因为题目不要求输出每次取走苹果的编号,因此,将上述5个苹果重新按照1-5号进行编号,就可以重复之前取苹果的方法,并且,关心的8号苹果,等同于剩余苹果总数第5号苹果什么时候被取走。
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
int n, all, day;
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n;
|
|
|
|
|
while (n) { // 剩余苹果数量
|
|
|
|
|
all++; // 新的一天
|
|
|
|
|
if (day == 0 && n % 3 == 1) day = all; // 判断编号n的苹果第几天被取走
|
|
|
|
|
n -= ceil(1.0 * n / 3);
|
|
|
|
|
// 上取整公式
|
|
|
|
|
// n = n - (n + 2) / 3; // 剩余苹果数
|
|
|
|
|
}
|
|
|
|
|
// 输出总天数 和 第n 个苹果第几天被取走
|
|
|
|
|
cout << all << " " << day << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|