【AGC011E】Increasing Numbers
题目
题目链接: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\) 个 \(111\cdots 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(n\log 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;
}

浙公网安备 33010602011771号