题目
题目链接:https://atcoder.jp/contests/agc011/tasks/agc011_e
我们说一个数是“递增的”,当且仅当对于它的任意相邻的两位都有左边小于等于右边。
如 (1558), (11), (3) 是递增的,(20170312)、(19260817) 就不是。
现在给你一个数 (n),问最少可以被表示成几个递增的数之和。
比如 (80 = 56 + 24),(2017 = 1349 + 668), (2019 = 1669 + 237 + 113)
(1 ≤ n ≤ 10^{500000})。
思路
任何一个递增的数,都可以表示成 (9) 个 (111cdots 1) 的和。注意这里可以是 (0) 个 (1)。
所以如果答案为 (k),那么有
[n=sum^{9k}_{i=1}frac{10^{a_i}-1}{9}
]
也就是
[9n+9k=sum^{9k}_{i=1}10^{a_i}
]
因为可以有 (0) 个 (1),所以其实就是要求出最小的 (k),使得 (9n+9k) 十进制下每一位之和不超过 (9k)。
二分,然后每次 (O(n)) 判断即可。时间复杂度 (O(nlog n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=500010;
int len,a[N+10],b[N+10];
char s[N+10];
void mul(int *a,int b)
{
int t=0;
for (int i=N;i>=1;i--)
{
a[i]=a[i]*b+t;
t=a[i]/10;
a[i]%=10;
}
}
void inc(int *a,int b)
{
int t=0;
for (int i=N;i>=1;i--)
{
a[i]=a[i]+(b%10)+t;
t=a[i]/10;
a[i]%=10;
b/=10;
}
}
bool check(int k)
{
memcpy(b,a,sizeof(a));
inc(b,9*k);
int sum=0;
for (int i=1;i<=N;i++) sum+=b[i];
return sum<=9*k;
}
int main()
{
scanf("%s",s+1);
len=strlen(s+1);
for (int i=1;i<=len;i++)
a[N-len+i]=s[i]-48;
mul(a,9);
int l=1,r=len,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid)) r=mid-1;
else l=mid+1;
}
cout<<r+1;
return 0;
}