【jzoj 3559】【luogu P2022】有趣的数(模拟)

有趣的数

题目链接:jzoj 3559 / luogu P2022

题目大意

设 Q(N,K) 为将 1~n 的正整数集合的元素按字典序排序,位置是第 K 个的数。
给你 K,M,要你找到最小的 N,使得 Q(N,K)=M。

思路

首先要发现一个性质:
如果位数足够大,排在前面的一定是 0 , 10 , 1000 , 10000 , . . . 0,10,1000,10000,... 0,10,1000,10000,...
那也就是说,如果出现了一个 n n n 位的这样的数,那它的答案一定是 $0
。这个要特判一下,下面在出现这些数会锅。

那接着我们来看,首先不难得到最小也要是原本的数,那原本的数它第几呢?
给一个算的方法:( 234 234 234 为例子)
一位数: 0 ∼ 2 0\sim2 02 2 − 0 + 1 = 1 2-0+1=1 20+1=1 个)
两位数: 10 ∼ 23 10\sim23 1023 23 − 10 + 1 = 14 23-10+1=14 2310+1=14 个)
三位数: 100 ∼ 234 100\sim234 100234 234 − 100 + 1 = 135 234-100+1=135 234100+1=135 个)
那不难想到,就是用 1 0 i − 1 10^{i-1} 10i1 减从高到低 i i i 位加一的结果累加起来。

那如果刚好是 K K K,那要的就是 M M M,如果小于 K K K,那说明无解。
那如果大于,就说明还要往后面放数字,继续加位数。
那我们继续以 234 234 234 为例:
四位数: 1000 ∼ 2339 ( 2340 − 1 ) 1000\sim2339(2340-1) 10002339(23401)
五位数: 10000 ∼ 23399 ( 23400 − 1 ) 10000\sim23399(23400-1) 1000023399(234001)
那不难看出,就是数字乘 1 0 i 10^{i} 10i(这次 i i i 是你多的位数,原本位数是 n n n)减去 1 0 i + n 10^{i+n} 10i+n 加一。

那如果还是小于 K K K,那就还要加,否则答案就在这之间。
你只要 1 0 i + n 10^{i+n} 10i+n 加上你剩余还差的部分再减 1 1 1 就可以了。

代码

#include<cstdio>
#define ll long long

using namespace std;

ll k, m, ten[101], tmp, kn;
ll now, addw, newadd;

int main() {
	ten[0] = 1;
	for (int i = 1; i <= 19; i++)
		ten[i] = ten[i - 1] * 10;
	
	scanf("%lld %lld", &k, &m);
	
	for (int i = 0; i <= 9; i++)//特判10...0的情况
		if (k == ten[i] && m != i + 1) {
			printf("0");
			return 0;
		}
	
	tmp = k;
	while (tmp) {
		kn++;
		tmp /= 10;
	}
	
	for (int i = kn; i >= 1; i--)//计算原本就这个数它前面有多少个(包括他)
		now += (k / ten[i - 1]) - ten[kn - i] + 1;
	
	if (now == m) printf("%lld", k);//刚好轮到他
		else if (now > m) printf("0");//位置过了
			else {
				tmp = k;
				while (now < m) {
					addw++;
					tmp *= 10;//不断加 0
					newadd = (tmp - 1) - ten[kn + addw - 1] + 1;//判断会新加多少在他前面
					if (now + newadd < m) now += newadd;//还可以加
						else {//不能加了,按着量计算出到哪刚好是他
							printf("%lld", ten[kn + addw - 1] + (m - now) - 1);
							return 0;
						}
				}
			}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值