【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;
}
posted @ 2021-10-18 11:35  stoorz  阅读(37)  评论(0)    收藏  举报