P6921 [ICPC2016 WF]Forever Young - 题解
TensorFlow_js · · 题解
0. 题意简述
给定
1. 做法详解
这里是一种基于二分和预处理的做法。
首先很容易想到,对于下限
具体就是把
如果满足“只包含十进制数字”的答案很稀疏的话,这个时间复杂度显然是过不去的,而事实就是这样。
我们仔细想想,会发现:如果过不去,说明
考虑预处理出从
这显然会牺牲一部分的正确性,但已经足以让我们通过
再考虑观察答案可能的分布位置。
我们试着打个暴力,可以发现,当
于是我们又多通过了两个测试点。
再考虑答案的集中趋势是不行了,于是我们试试看极限情况
我们会发现,在
加上上述三个预处理即可通过本题。
2. 代码
#include<bits/stdc++.h>
template<typename T1, typename T2>
constexpr auto max (T1 a, T2 b) -> decltype(a + b) {
if (a > b)return a;
return b;
}
using namespace std;
using ll = long long;
constexpr ll maxn1 = 999999, maxn2 = 1000, maxms = 114.514;
//分别为:从 10 开始枚举时的上限;sqrt(y) 附近的波动范围;暴力枚举的时间上限
ll change (ll l, ll b, ll y) {//把 l 当作 b 进制数,然后把 l 转回 10 进制
if (l < 10)return l;
auto a = change (l / 10, b, y);
if (a >= y / b + 1 || !~a)return -1;//细节:如果直接返回 10 进制的值会溢出,所以判断是否大于 y,若是则直接返回 -1
return a * b + l % 10;
}
bool check (ll l, ll b) {//判断是否满足“只包含十进制数字”
if (l < b)return l < 10;
return check (l / b, b) && l % b < 10;
}
inline auto gettime ( ) {//获取时间(精确到毫秒)
return chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now ( ).time_since_epoch ( )).count ( );
}
int main ( ) {
ll y, l, ans = -1, L = 10, R = 1e18, m, beg = gettime ( );
//分别为:题目中的 y 与 l;预处理的答案;二分答案的上下界;二分答案的中值;程序开始运行的时间
cin >> y >> l;R = y;
while (L < R) {//二分答案上界
m = L + R + 1 >> 1;
if (change (l, m, y) != -1 && change (l, m, y) <= y)L = m;
else R = m - 1;
}
for (ll i = 10;i <= maxn1 && i <= L;i++)//预处理1
if (check (y, i))ans = max (ans, i);
if (sqrt (y) > maxn2)//细节:若 sqrt(y) 小于 maxn2,则二分答案已经可以出结果,预处理反而会 RE
for (ll i = max (sqrt (y) / 2 - maxn2 / 3, 0);i <= sqrt (y) / 2 + maxn2 / 3 && i <= L;i++)//预处理2
if (check (y, i))ans = max (ans, i);
if (y > maxn1)
for (ll i = maxn1;y / i < L && i>1 && y / i > 10;i--)
if (check (y, y / i))ans = max (ans, y / i);//预处理3
while (gettime ( ) - beg < maxms) {//带卡时的暴力枚举
if (check (y, L)) {
cout << L << endl;
return 0;
}
L--;
}
cout << ans << endl;
return false;
}
3.总结
二分答案是一个很好用的方法,并且如果能看出一些答案的分布规律的话可以结合使用。
4.UPD
[2022-10-13 22:44] 优化了代码的可读性。