SECHS

题目描述

对于给定的正整数N,我们把[1, N]中的整数按照字符串的字典序排序得到N 项数列A(N)。

例如,N = 11的时候,A(N) = {1, 10, 11, 2, 3, 4, 5, 6, 7, 8, 9}。二元函数 Q(N, K)的定义域为N,

K∈Z+ 且 N≥K,其值为K 在A(N) 中的位置。例如从上面给出的A(11)中可以看出Q(11, 2) = 4。

现在你的任务是,对于给定的正整数 K 和M,求最小的正整数N 满足Q(N, K) = M。
输入格式
          仅有一行,包含两个正整数K 和M。
输出格式
          输出一个正整数N 表示答案。如果不存在这样的N,输出0。
样例输入 1
  12 7
样例输出1
  102
样例输入 2
  100000001 1000000000
样例输出2
  100000000888888879
数据范围与约定
  对于30% 的数据,满足M, K≤100。
  对于 100% 的数据,满足1≤M, K≤10^9。
乍一看.......没思路,但是好像是数学;
再乍......果然是数学,没思路(@[email protected]);
先说一下,这道题最后是我看题解会的,这里就讲一下题解的思路(题解太简单了,简直虐尽天下像我一样的蒟蒻)

首先,先和大家普及一下有关字典序的知识,
对于数字n,它第一次出现在数列中时,它的位置的计算方法(以12345为例):
先拆位,将12345拆为(1),(12),(123),(1234),(12345)。
然后用每一位的数减去与其位数相等的最小数+1,为其在这一位上的位置
1——1 (1-1+1=1)
12——10 (12-10+1=3)
123——100 (123-100+1=24)
1234——1000 (1234-1000+1=235)
12345——10000 (12345-10000+1=2346)
最后,将得到的所有数加起来,得到 (1+3+24+235+2346=2609);
就是这个数第一次出现时他的位置(证明请看推导过程)
然后......在代码里讲吧

program ex02;
var ten:array[0..30] of int64;
      m,k,p:int64;
procedure init; //读入,预处理
var i:longint;
begin
  ten[0]:=1;
  for i:=1 to 18 do ten[i]:=ten[i-1]*10;
  readln(m,k);
end;

function rank(x:int64):int64; //求某数在数列中第一次出现的位置;(用上面公式)
var i:int64;
begin
  i:=1;
  while ten[i]<=x do inc(i); //每一位分别处理,再依次相加;
  exit(x-ten[i-1]+1);
end;

function solve:int64;
var i,len,r,n:int64;
begin
  n:=m; r:=0;
  while n>0 do //求输入的数在数列中第一次出现的位置;
  begin
    inc(r,rank(n));
    n:=n div 10;
  end;
  if r=k then exit(m); //正好相等,直接输出;
  if r>k then exit(0); //小,不可能达成;
  len:=0; n:=m;
  while n>0 do //求位数
  begin
    inc(len);
    n:=n div 10;
  end;
  dec(k,r); i:=0; //求中间差了几个数
  while true do
  begin
    inc(i);
    if k<=rank(ten[i]*m)-1 then exit(k+ten[i+len-1]-1); //关键的一步,,,卡了我好长时间,,,讲解在下面
    dec(k,rank(ten[i]*m-1));
  end;
end;

function check(m:int64):int64;
begin
  check:=0;
  while m>=1 do
  begin
    if (m<>1) and (m mod 10<>0) then exit(-1);
    m:=m div 10;
    inc(check);
  end;
end;
begin
  assign(input,‘sec.in‘); reset(input);
  assign(output,‘sec.out‘)‘ rewrite(output);
  init;
  p:=check(m);
  if (p>0) then
  begin
    if (p=k) then
      writeln(m)
    else
      writeln(0);
  end
  else
    writeln(solve);
  close(input);
  close(output);
end.

key点:

我们知道要将这个题所求的范围置于某一特定的位数

那么,这个位数则么得到?

易得一点:1,10,100,1000,10000.....出现后,它们前面不会多出数字,那么,我们只需要知道rank(k*10^d)>m>rank(10^l) 的l的值

那么,m-rank(k)就是需要在k前面插入的数的个数

如果在 10^l~k*10^d中的数用不完就填满了,直接输出, 但如果不够,就将其扩大10倍,进行下面的操作,直到填完为止。

~\(≧▽≦)/~

时间: 2024-10-10 05:58:00

SECHS的相关文章

9-15考试题目程序作业

然而有素质的我选择把题目也粘在上面,喜欢的同学可以做一下 支付宝(pay.pas/c/cpp)题目描述做完前七次Nescafé 模拟赛之后,你已经欠了一大笔电费.现在收电费的已经把你堵在了家里.当然了,你并不是打算一直拖欠电费的人,你只是如果不能用最少的纸币张数凑出电费金额的话会感到十分不爽.本来这是一件很简单的事,但是你平常很少使用标准的纸币,而是习惯去银行领一本支票簿,在每一张上填上自己喜欢的金额然后把这些东西用作纸币付账,这就使问题变得复杂了起来.现在你知道你一共填写了N种金额的支票,第i

Learn Prolog Now 翻译 - 第四章 - 列表 - 第四节,练习题和答案

练习题4.1 Prolog将会如何回答下面的查询? 1. [a, b, c, d] = [a, [b, c, d]]. 2. [a, b, c, d] = [a | [b, c, d]]. 3. [a, b, c, d] = [a, b, [c, d]]. 4. [a, b, c, d] = [a, b | [c, d]]. 5. [a, b, c, d] = [a, b, c, [d]]. 6. [a, b, c, d] = [a, b, c | [d]]. 7. [a, b, c, d] =