JZOJ3559-Luogu P2022-有趣的数

6 篇文章 0 订阅
这篇博客探讨了一个数学问题,涉及在1到NNN的正整数集合中,如何找到使数字KKK在字典序排列下位于位置MQ(N,K)=MQ(N,K)=MQ(N,K)=M的最小NNN值。通过分析KKK前面的数字数量,确定KKK的位置,并根据位置与M的关系,计算NNN的值。博主提供了详细的解题思路和C++代码实现。
摘要由CSDN通过智能技术生成

Description&Data Constraint

让我们来考虑 1 到 N N N 的正整数集合。让我们把集合中的元素按照字典序排列,例如当 N = 11 N=11 N=11 时,其顺序应该为: 1 , 10 , 11 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 1,10,11,2,3,4,5,6,7,8,9 1,10,11,2,3,4,5,6,7,8,9

定义 K K K N N N 个数中的位置为 Q ( N , K ) Q(N,K) Q(N,K),例如 Q ( 11 , 2 ) = 4 Q(11,2)=4 Q(11,2)=4。现在给出整数 K K K M M M,要求找到最小的 N N N,使得 Q ( N , K ) = M Q(N,K)=M Q(N,K)=M

1 ≤ K , M ≤ 1 0 9 1\le K,M\le10^9 1K,M109

Solution

首先从特殊往一般想。

考虑当 K = 1 0 i K=10^i K=10i 的时候的情况。由于其字典序最小,因此位置是固定的,即 i + 1 i+1 i+1,那么只要 M M M 不是这个位置都是无解。

既然是位置,可以考虑先求出 K K K 的最小位置。

思考在 K K K 前面的会有多少。假设 K = 114514 K=114514 K=114514

  1. 114514 − 100000 + 1 = 14515 114514-100000+1=14515 114514100000+1=14515。这是在位数一样的情况下。
  2. 11451 − 10000 + 1 = 11452 11451-10000+1=11452 1145110000+1=11452。这是在少一位的情况下。
  3. 1145 − 1000 + 1 = 1146 1145-1000+1=1146 11451000+1=1146。同上,以此类推。
  4. 114 − 100 + 1 = 15 114-100+1=15 114100+1=15。同上,以此类推。
  5. 11 − 10 + 1 = 2 11-10+1=2 1110+1=2。同上,以此类推。
  6. 1 − 1 + 1 = 1 1-1+1=1 11+1=1。同上,以此类推。

因此可以发现,在比 K K K i i i 位的情况下,将会有(设 K K K 的长度是 l e n len len ⌊ K 1 0 i ⌋ − 1 0 l e n − i + 1 \lfloor\dfrac{K}{10^i}\rfloor-10^{len-i}+1 10iK10leni+1 个数的字典序比 K K K 小。

统计出 K K K 的最小位置 n u m num num 后,将 n u m num num M M M 进行比较。

n u m = M num=M num=M,那么只要出现了 K K K 就可以使得 K K K 的位置是 M M M,因此 N N N 的最小值就是 K K K

n u m > M num>M num>M,就无解了,因为 n u m num num K K K 的最小位置。不存在其他的位置比 n u m num num 还要小。

n u m < M num<M num<M,就说明还有比 K K K 前面的数,但是位数小于等于 K K K 的已经穷举完了,因此肯定有位数大于 K K K 的在 K K K 前面。逐步增加位数,同时更新最小位数。当某个时刻 n u m ≥ M num\ge M numM,先将 n u m num num 减回去,算出 M M M n u m num num 之间的差。再加上 1 0 x 10^{x} 10x ,这个 x x x 是当前位数。最后减去1(因为从0开始算),就是 N N N 的大小。

Code

#include<cmath>
#include<cstdio>
using namespace std;
int len;
long long k,m,num,p[20];
int main()
{
	scanf("%lld%lld",&k,&m);
	p[0]=1;
	for (int i=1;i<=18;++i)
		p[i]=p[i-1]*10;
	for (int i=0;i<=18;++i)
		if (k==p[i])
		{
			if (m==i+1) printf("%lld\n",k);
			else printf("0\n");
			return 0;
		}
	len=(int)log10(k);
	for (int i=len;i>=0;--i)
		num+=k/p[i]-p[len-i]+1;
	if (num==m)	printf("%lld\n",k);
	else if (num>m) printf("0\n");
	else
	{
		for (int i=1;i<=10;++i)
		{
			k*=10;
			num+=k-p[len+i];
			if (num>=m)
			{
				num-=k-p[len+i];
				printf("%lld\n",m-num+p[len+i]-1);
				return 0;
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值