#P3358. 第1题-落叶数量
-
1000ms
Tried: 105
Accepted: 34
Difficulty: 2
所属公司 :
科大讯飞
时间 :2025年8月9日
-
算法标签>模拟
第1题-落叶数量
思路
将一年 12 个月的“掉落量”形成固定模式:
-
非刮风月掉
m,刮风月掉2m。 -
一年总掉落量:
Y=(12−q)⋅m+q⋅(2m) = (12+q)⋅m
做法:
-
先整年跳跃:设可完整经过的年数
k=⌊Yn−1⌋
跳过
k年(12k个月),剩余n ← n - kY。用
(n-1)//Y可以避免“正好整年结束”的边界误差,同时不影响最优性。 -
对下一年只需在 最多 12 个月 内顺序扫一遍,用前缀和(或简单累加)找到第一个使累计掉落 ≥ 剩余
n的月份。
算法步骤
- 构造数组
drop[1..12]:普通月为m,[p, p+q-1]为2m。 - 计算
Y = sum(drop);k=(n-1)//Y;ans_month = 12*k;rem = n - k*Y。 - 从 1 月到 12 月累加
drop[i],首次使累计 ≥rem时,ans_month += i即为答案。
复杂度分析
- 时间:
O(12)(整年跳过后只需扫一年的 12 个月)。 - 空间:
O(1)。
代码
Python
# 读取 n, m, p, q,计算最少月份数
import sys
def solve():
data = sys.stdin.read().strip().split()
if not data:
return
n, m, p, q = map(int, data[:4])
# 构造一年 12 个月的掉落量
drop = [m] * 12
for i in range(p - 1, p - 1 + q):
drop[i] = 2 * m
yearly = sum(drop) # Y = (12 + q) * m
years = (n - 1) // yearly # 可整年跳过的年数
months = years * 12
rem = n - years * yearly # 跳过后剩余的叶子数
# 在下一年内顺序找首个前缀和 >= rem 的月份
acc = 0
for i in range(12):
acc += drop[i]
months += 1
if acc >= rem:
print(months)
return
if __name__ == "__main__":
solve()
Java
// 计算最少月份数:前缀和 + 整年跳跃
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
if (!sc.hasNextLong()) return;
long n = sc.nextLong(); // 初始叶子数
long m = sc.nextLong(); // 普通月每月掉落
int p = sc.nextInt(); // 刮风期起始月(1..12)
int q = sc.nextInt(); // 刮风期持续月数
long[] drop = new long[12];
Arrays.fill(drop, m);
for (int i = p - 1; i < p - 1 + q; i++) drop[i] = 2 * m;
long yearly = 0;
for (long x : drop) yearly += x; // 一年总掉落量
long years = (n - 1) / yearly; // 整年跳过
long months = years * 12;
long rem = n - years * yearly; // 剩余叶子
long acc = 0;
for (int i = 0; i < 12; i++) {
acc += drop[i];
months++;
if (acc >= rem) {
System.out.println(months);
return;
}
}
}
}
C++
// 计算最少月份数:前缀和 + 整年跳跃
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n, m;
int p, q;
if (!(cin >> n >> m >> p >> q)) return 0;
vector<long long> drop(12, m); // 一年 12 个月的掉落量
for (int i = p - 1; i < p - 1 + q; ++i) drop[i] = 2 * m;
long long yearly = 0;
for (auto x : drop) yearly += x; // 一年总掉落
long long years = (n - 1) / yearly; // 可整年跳过的年数
long long months = years * 12;
long long rem = n - years * yearly; // 剩余
long long acc = 0;
for (int i = 0; i < 12; ++i) {
acc += drop[i];
++months;
if (acc >= rem) {
cout << months;
return 0;
}
}
return 0;
}
题目内容
牛牛望着窗外的树叶发出疑问:这棵老树什么时候才能掉完所有的叶子?
1.在一月初,树上有n片叶子;
2.每月会掉落m片叶子;
3.每年从p月开始会有q个月的刮风期;
4.刮风期每月会掉落2×m片叶子。
当叶子数不大于零时,视为所有叶子已掉落。现在请你编写一个程序,帮助牛牛计算:落下所有叶子需要几个月。
输入描述
在一行上输入四个整数,依次为:
- n(1≦n≦106),表示树上的叶子数;
- m(1≦m≦n),表示每月会掉落的叶子数;
- p(1≦p≦12),表示从几月开始刮风;
- q(1≦q≦13−p),表示刮风期持续的月份数,保证刮风期不会持续多年。
输出描述
输出一个整数,表示使叶子全部掉落所需的月数。
样例1
输入
10 1 1 3
输出
7
说明
在这个样例中:
- 一月为刮风期,掉落2片,剩余8片;
- 二月为刮风期,掉落2片,剩余6片;
- 三月为刮风期,掉落2片,剩余4片;
- 四月为常规期,掉落1片,剩余3片;
- 五月为常规期,掉落1片,剩余2片;
- 六月为常规期,掉落1片,剩余1片;
- 七月为常规期,掉落1片,剩余0片;
因此总共需要3+4=7个月。
样例2
输入
50 10 4 4
输出
4
说明
在这个样例中:
-
一月、二月、三月每个月落10片;
-
四月、五月、六月、七月为刮风期,每月落20片;
因此,所有的叶子都会在第4个月落下。
样例3
输入
100 2 5 8
输出
31
说明
每年的五月至十二月为八个月的刮风期。