B4079 [CSP-X2019 山东] 金币
欢迎报名洛谷网校,期待和大家一起进步!
本题考查模拟、数学与二分答案。
(模拟法) 以周为单位进行模拟,每次计算一周能获取多少枚金币并且累加求和,直到出现了拿了当前周金币之后金币总数
在本题的数据范围内,该做法勉强可以通过,原因是这个数据范围下最大的周数大约是 long long 类型。
参考代码(部分):
while (true) {
if (week * 7 + cnt >= n) {
int t = 0;
while (week * t + cnt < n)
t++;
cout << day + t << endl;
break;
} else {
cnt += 7 * week;
week++;
day += 7;
}
}
(二分答案法) 该做法会显著更优,需要读者了解二分答案这一算法。
实际上,对于一个日期
- 已经经过的完整的周数:
y=\lfloor \dfrac{x}{7} \rfloor (即x / 7); - 在完整的周内,一共可以获得:
7\times 1+7\times 2+7\times 3+\dots+7\times y 枚金币。- 根据等差数列求和公式,可以写为
7\times \dfrac{y(y+1)}{2} 枚金币。
- 根据等差数列求和公式,可以写为
- 还有
x \bmod 7 (x % 7)天是在第(y+1) 周,因此可以获得(y+1)\times (x\bmod 7) 枚金币。
因此总共的获得金币枚数是:
但是显然我们不能直接枚举每一天,因为天数可能会过多,达到
具体而言,二分在第几天可以获得总计
经过测试 long long 类型乃至 unsigned long long 类型的表达上限。在代码中采用了
参考代码:
bool check(long long mid) { //mid:天数
long long x = mid / 7; //计算过了多少个整的周
long long cnt = 7 * (1 + x) * x / 2; //计算完整的周内获得的金币数
cnt += (x + 1) * (mid % 7); //计算剩余的天数获得的金币数
return cnt >= n;
}
while (l <= r) {
long long mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}