P16605 [SYSUCPC 2025] Sum

思路

数据范围一眼根号分治。
首先考虑暴力枚举进制,也就是进制转换,对于 $10$ 进制转 $k$ 进制,不断取 $n\bmod k$ 再除 $k$ 直到 $n=0$ 即可。
时间复杂度 $O(B\log n)$,其中 $B$ 是阈值。
然后考虑如何得到一个 $\frac{n}{B}$ 状物,分析上面的方法,发现若 $k>\sqrt n$,那么只能进行两次除法就归零,会取两个值: $n\bmod k,\lfloor\frac{n}{k}\rfloor$。
那么对于既定的 $k$,贡献则是 $n\bmod k+\lfloor\frac{n}{k}\rfloor$。
推一下式子:
原式 $=n-\lfloor\frac{n}{k}\rfloor\times k+\lfloor\frac{n}{k}\rfloor=n-(k-1)\times \lfloor\frac{n}{k}\rfloor$。
枚举 $\lfloor\frac{n}{k}\rfloor$,要使得原式子在既定的 $\lfloor\frac{n}{k}\rfloor$ 下最小,就是使得 $k-1$ 最大,只需找到 $k$ 最大,那么 $k=\lfloor\frac{n}{\lfloor\frac{n}{k}\rfloor}\rfloor$。
这样时间复杂度就是 $O(B\log n+\frac{n}{B})$。
$B$ 至少取 $\sqrt n$,那我们就取 $\sqrt n$。
发现这样还是无法通过,所以考虑优化第一部分,直接在此时答案比已经得到的答案不优的时候中断就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include<bits/stdc++.h>
#define fir first
#define sec second
#define int long long
#define pii pair<int,int>
#define double long double
#define fep(i,s,e) for(int i=s;i<e;i++)
#define pef(i,s,e) for(int i=s;i>e;i--)
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define per(i,s,e) for(int i=s;i>=e;i--)
namespace FastIO{
template<typename T>inline void read(T &x){
x=0;int f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);x*=f;
}
inline void read(char &c){
c=getchar();
while(isspace(c))c=getchar();
}
inline void read(std::string &s){
s.clear();char c=getchar();
for(;isspace(c);c=getchar());
for(;!isspace(c)&&c!=EOF;c=getchar())s+=c;
}
template<typename T,typename...Args>
void read(T &x,Args&...args){
read(x);read(args...);
}
template<typename T>void print(T x){
if(x<0)x=-x,putchar('-');
if(x>9)print(x/10);
putchar((x%10)^48);
}
inline void print(char c){
putchar(c);
}
inline void print(std::string s){
fep(i,0,s.size())putchar(s[i]);
}
inline void print(const char*s){
while(*s)putchar(*s++);
}
template<typename T,typename...Args>
void print(const T &x,const Args&...args){
print(x);print(args...);
}
}
using namespace std;
using namespace FastIO;
int T,n,R,ans;
int work(int n,int x){
int res=0;
while(n>0){
res+=n%x;
n/=x;
if(res>ans)return ans+1;
}
return res;
}
signed main(){
read(T);
while(T--){
ans=100;
read(n,R);
int B=sqrt(n)+1;
rep(i,2,min(B,R)){
ans=min(ans,work(n,i));
}
per(i,B,1){
int a=(int)floor(1.0*n/i);
if(a>R)break;
ans=min(ans,n-(n/a)*(a-1));
}
ans=min(ans,work(n,R));
print(ans,'\n');
}
}