【题解】George and Number

题目信息

题目来源:Codeforces Round #227 (Div. 2);

翻译来源:洛谷;

在线评测地址:原题洛谷 Remote

运行限制:(1.00 ext{s}/256 ext{MiB})

题目描述

George is a cat, so he really likes to play. Most of all he likes to play with his array of positive integers (b). During the game, George modifies the array by using special changes. Let's mark George's current array as (b_{1},b_{2},...,b_{|b|}) (record |b|∣b∣ denotes the current length of the array). Then one change is a sequence of actions:

乔治喜欢对他的数列 (b) 进行操作。 我们将乔治的数列表示成 (b_1, b_2, ..., b_{|b|}) (其中 (|b|) 表示数列 (b) 的长度)。一次操作分为以下几个步骤:

  • Choose two distinct indexes (i) and (j) ((1<=i,j<=|b|; i≠j)), such that (b_{i}>=b_{j}).
  • Get number (v=operatorname{concat}(b_{i},b_{j})), where (operatorname{concat}(x,y)) is a number obtained by adding number (y) to the end of the decimal record of number (x). For example, (operatorname{concat}(500,10)=50010), (operatorname{concat}(2,2)=22).
  • Add number (v) to the end of the array. The length of the array will increase by one.
  • Remove from the array numbers with indexes (i) and (j). The length of the array will decrease by two, and elements of the array will become re-numbered from (1) to current length of the array.
  • 选择两个不同的数 (i)(j)(1le i, jle |b|)(i eq j)),满足 (b_ige b_j)
  • 定义数 (v = operatorname{concat}(bi, bj)),其中 (operatorname{concat}(x, y)) 是将数 (y) 连接在数 (x) 后面形成的新数。举个例子,(operatorname{concat}(500, 10) = 50010)(concat(2, 2) = 22)
  • 将数 (v) 加到数列的末尾,数列长度增加 (1)
  • 将数列中第 (i) 项和第 (j) 项删除。数列长度缩短 (2),并且数列会被重新编号为 (1) 到当前数列长度。

George played for a long time with his array (b) and received from array bb an array consisting of exactly one number (p) . Now George wants to know: what is the maximum number of elements array (b) could contain originally? Help him find this number. Note that originally the array could contain only positive integers.

乔治进行了太多的操作使得数列 (b) 使得数列最终只存在一个数 (p)。现在乔治想知道,数列 (b) 最初最多能有几个数?帮他求出答案。注意数列最初只能包含正整数。

输入格式

The first line of the input contains a single integer (p) ( (1le p<10^{100000}) ). It is guaranteed that number (p) doesn't contain any leading zeroes.

第一行包含一个整数 (p)($1le p < 10^{100000})。 保证数字 (p) 最高位不为 (0)

输出格式

Print an integer — the maximum number of elements array (b) could contain originally.

输出一个整数,即数列 (b) 最初的最长长度。

分析

给定一个数 (p)(p) 由若干次拼接操作构成,每次拼接操作要求前面的数要大于等于后面的数,求原先最多有多少数,这些数都不允许存在前导零。

这个拼接时的大小要求十分让人讨厌,我们可以令拼接操作从左往右进行,这样左边的数尽可能大,而右边尽可能小。

同时,数是不允许存在前导零的,所以我们每次遍历,都往后一直到没有后继零为止,这个数与前面已经拼接完成的数进行大小比较。有两种情况:

  1. 后面的数比前面小或相等,就拼接起来;
  2. 后面的数比前面的数大:如 23,此时我们要将前面的数和后面的数合为一个整体,注意到这时这个数就不是拼接而来的。比如 19992000 就是一个数,不是由 19992000 构成的。

这种做法显然正确,证明如下:

设当前这一段为 (x),前面已经完成的拼接段为 (p_1),后面未合并的 (p_2)

(p_1) 已经无法与 (x) 拼接,如果 (x) 不和 (p_1) 合成一个整体,则:

后面 (x) 一定与 (p_2) 拼接,否则一定没有合成整体优,但这样产生一个新数 (x^prime>x),更加无法和 (p_1) 拼接。也没有直接合成优。

这样就可以了。

注意到有一个小 trick,在判整数大小时,不用傻乎乎地去写高精度,只需要判长度和首位即可,自证不难。

Code

#include <cstdio>
using namespace std;

const int max_n = 100000;
char a[max_n+1];

int main()
{
	int n, lp = 114514, ptr, cnt = 0; // 初始需要一个长度无限大的数

	scanf("%s", a);
	for (n = 0; a[n]; )
	{
		ptr = n + 1;
		while (a[ptr] && a[ptr] == '0') // 去除后继零
			ptr++;
		
		if (lp > ptr - lp || (lp == ptr - lp && a[0] >= a[lp]))
		{
			n = lp = ptr; // 可以拼接
			cnt++; // 记上数量
		}
		else // 否则直接合成
		{
			n = lp = ptr;
			cnt = 1; // 只能算一个
		}
	}

	printf("%d
", cnt);

	return 0; // 然后就 AC 了、
}

非常感谢您读完此文章。

如果您喜欢这篇文章,请点一个赞支持一下博主。

如果您对此文有意见,欢迎在评论区留下你的看法,5ab 会尽力达到。

如果您真的不喜欢这篇文章,也请不要喷,5ab 欢迎私信反馈意见。

原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-cf387c.html