题目相关
题目链接
AtCoder Regular Contest 109 B 题,https://atcoder.jp/contests/arc109/tasks/arc109_b。
Problem Statement
Snuke is visiting a shop in Tokyo called 109 to buy some logs. He wants n logs: one of length 1, one of length 2, ..., and one of length n. The shop has n+1 logs in stock: one of length 1, one of length 2, …, and one of length n+1. Each of these logs is sold for 1
yen (the currency of Japan).
He can cut these logs as many times as he wants after buying them. That is, he can get k logs of length L1,…,Lk from a log of length L, where . He can also throw away unwanted logs.
Snuke wants to spend as little money as possible to get the logs he wants. Find the minimum amount of money needed to get n logs of length 1 to n.
Input
Input is given from Standard Input in the following format:
n
Output
Print the minimum amount of money needed to get n logs of length 1 to n.
Samples1
Sample Input 1
4
Sample Output 1
3
Explaination
One way to get the logs he wants with 3 yen is:
- Buy logs of length 2, 4, and 5.
- Cut the log of length 5 into two logs of length 1 each and a log of length 3.
- Throw away one of the logs of length 1.
Samples2
Sample Input 2
109109109109109109
Sample Output 2
109109108641970782
Constraints
题解报告
题目翻译
Snuke 到东京的 109 商店去买一些木头。他需要买 n 个木头:一个长度为 1,一个长度为 2,...,和一个长度为 n。商店里有 n+1 个木头:1个长度为 1,一个长度为 2,...,和一个长度为 n+1。每个木头都卖 1 日元,不管长短。Snuke 可以将买到的木头切割成为他需要的长度,这样他就可以获得 k 个木头,例如长度为 L 的木头,可以切割为长度为 ,也就是
,他还可以任意丢弃木头。
求 Snuke 可以使用的最小代价达到他的要求是什么。
题目分析
看到求结果,就知道这是一个数学题。
根据题目,我们知道 Snuke 需要获得 长度的木头每个一跟,因此总木头长度为
。而商店提供长度为
的木头每个一跟,每个木头的价格都是 1 日元,因此我们只需要找到最大 x,使得数列
的总和
大于等于 Snuke 需要的数列总和
即可。这样,我们可以使用二分查找找到这个最大的 X。这样我们需要的实际木头数量为 n+1-x+1。
既然确定可以使用二分查找,我们还需要确定使用二分模板,我们可以非常容易得到,这是一个二分查找带左端点的模板。二分查找模板可以查看我老的 Blog,https://blog.youkuaiyun.com/justidle/article/details/104527596?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160674621019724848159656%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=160674621019724848159656&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v1~rank_blog_v1-13-104527596.pc_v1_rank_blog_v1&utm_term=%E4%BA%8C%E5%88%86&spm=1018.2118.3001.4450。
数据分析
根据题意,n 的最大值为 1e18,因此 n*(n+1) 的最大值为 1e18*1e18=1e36。这样就尴尬了,g++ 自带最大数据为 unsigned long long 也只能表示 1e19 的数据。好在 AtCoder 提供了最大的数据为 __int128_t,也就是 128b,比 long long 的 64b 大了一倍。
AC 参考代码
//https://atcoder.jp/contests/arc109/tasks/arc109_b
//B - log
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定义 __LOCAL
//#define __LOCAL
typedef long long LL;
LL n;
bool check(LL x) {
__int128_t sum1=((__int128_t)n)*(n+1);
__int128_t sum2=(((__int128_t)n)+2-x)*(n+1+x);
return sum2>=sum1;
}
int main() {
#ifndef __LOCAL
//这部分代码需要提交到OJ,本地调试不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
cin>>n;
LL left=0;
LL right=n+1;
LL mid;
while (left<right) {
mid = (left+right+1)/2;
if (check(mid)) {
left=mid;
} else {
right=mid-1;
}
}
cout<<n+2-left<<"\n";
#ifdef __LOCAL
//这部分代码不需要提交到OJ,本地调试使用
system("pause");
#endif
return 0;
}
时间复杂度
O(logN)。
空间复杂度
O(1)。
换一个思路二分
由于使用和来判断数据量超过了 long long 的范围,我们换一个思路来二分查找。商店可以提供的序列是 ,Snuke 需要的序列是
,我们将这两个序列的总和做一个减法,可得
。
也就是说,假设最大的 X,使得 Snuke 需要的序列为 满足要求。哪么多余的序列为
,也就是说这个 X 满足
。
根据题目给出的数据 n 的最大值为 1e18。当 n 越来越大,x 越趋近 0。因此我们可以考虑从 0 ~ 2e9 之间来查找 x。这样 2e9*2e9=4e18,而 long long 的大小为 9e18,这样使用 long long 就可以满足要求了,不需要使用 128b 的数据。
这样我们找到了 x,对应的答案就是 n-1+x。
特么都是满满的数学,想了我一个晚上,数学还是太烂。这不就是所谓的反问题,我真是个🐷。
简化 AC 代码
//https://atcoder.jp/contests/arc109/tasks/arc109_b
//B - log
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定义 __LOCAL
//#define __LOCAL
typedef long long LL;
LL n;
bool check(LL x) {
LL sum1=x*(x+1)/2;
LL sum2=n+1;
return sum2>=sum1;
}
int main() {
#ifndef __LOCAL
//这部分代码需要提交到OJ,本地调试不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
cin>>n;
LL left=0;
LL right=2e9;
LL mid;
while (left<right) {
mid = (left+right+1)/2;
if (check(mid)) {
left=mid;
} else {
right=mid-1;
}
}
cout<<n+1-left<<"\n";
#ifdef __LOCAL
//这部分代码不需要提交到OJ,本地调试使用
system("pause");
#endif
return 0;
}
还好,时间有所提升,因为计算量小了。时间复杂度和空间复杂度之类,没有任何变化。