Consider integer numbers from 1 to n. Let us call the sum of digits of an integer number its weight. Denote the weight of the number x as w(x).

Now let us order the numbers using so called graduated lexicographical ordering, or shorter grlex ordering. Consider two integer numbers a and b. If w(a) < w(b) then a goes before
b in grlex ordering. If w(a) = w(b) then a goes before b in grlex ordering if and only if the decimal representation of a is lexicographically smaller than the decimal representation of b.

Let us consider some examples.

  • 120 < grlex4 since w(120) = 1 + 2 + 0 = 3 < 4 = w(4).
  • 555 < grlex78 since w(555) = 15 = w(78) and "555" is lexicographicaly smaller than "78".
  • 20 < grlex200 since w(20) = 2 = w(200) and "20" is lexicographicaly smaller than "200".

Given n and some integer number k, find the position of the number k in grlex ordering of integer numbers from 1 to n, and the k-th number in this ordering.

Input

There are several lines in the input file, and each line stands two integers n and k (1 <= k <= n <= 1018). A line with n = k = 0 ends up the input.

Output

For each line in the input, output one line in the output file. First print the position of the number k in grlex ordering of integer numbers from 1 to n, and then the integer that occupies
the k-th position in this ordering.

Sample Input

20 10
0 0

Sample Output

2 14


题意:先把1~n内的数依照数位和排一次序。然后数位和相等的依照字典序拍一次序,然后输出k的位置和第k个位置的数

这道题在高逸涵的论文《数位计数问题解法研究》中有
他给出了5个函数

1. getSum1(int L, int sum); 数字和为 sum 的 L 位数字的个数(以0为前缀的也算数)

2. getSum2(LL n, int sum); 返回 1~n 中数字和为 sum 的数的个数

3. getSum3(LL n, LL prefix, int sum); 返回 1~n 中数字和为 sum 前缀为 prefix 的数的个数

4. getSum4(LL n, LL k, int sum); 返回 1~n 中数字和为 sum 且字典序小于k的数的个数

5. getSum5(LL n, LL k); 返回 k 在 1~n 中的位置

用这五个函数解决这道题
在ZOJ提交后吃惊的发现自己排在d第2,并且与第1的执行时间和内存是一样的,仅仅是他是08年提交的。我是14年提交的


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define ll __int64
#define up(i,x,y) for(i=x;i<=y;i++)
#define down(i,x,y) for(i=x;i>=y;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define w(x) while(x)
ll n,k;
ll dp[20][200];
//数字和为 sum 的 L 位数字的个数(以0为前缀的也算数)
ll getSum1(int L, int sum)
{
if(sum>9*L || sum<0 || L<0)
return 0;
if(dp[L][sum])
return dp[L][sum];
if(!L&&!sum)
return 1;
int i;
up(i,0,9)
{
if(sum-i<0)break;
dp[L][sum]+=getSum1(L-1,sum-i);
}
return dp[L][sum];
}
// 返回 1~n 中数字和为 sum 的数的个数
ll getSum2(ll n, int sum)
{
int bit[20],i,len=0,j;
ll ans=0;
w(n)
{
bit[len++]=n%10;
n/=10;
}
down(i,len-1,0)
{
up(j,0,bit[i]-1)
ans+=getSum1(i,sum--);
}
ans+=getSum1(0,sum);
return ans;
}
//返回 1~n 中数字和为 sum 前缀为 prefix 的数的个数
ll getSum3(ll n,ll prefix,int sum)
{
char sn[20],sp[20];
int ln,lp,i,j;
ll ans = 0;
sprintf(sn,"%I64d",n);//将数字转化为字符串
sprintf(sp,"%I64d",prefix);
ln=strlen(sn),lp=strlen(sp);
up(i,0,lp-1)
sum-=sp[i]-'0';
up(i,0,lp-1)
if(sn[i]!=sp[i])
break;
if(i<lp)
{
if(sn[i]<sp[i]) ln--;//假设前缀的这一位大于n。那么从第二高位匹配,看作n的长度减去1
down(i,ln-lp,0)
ans+=getSum1(i,sum);
return ans;
}
ll tem=0;
up(i,lp,ln-1)
tem=tem*10+sn[i]-'0';
ans+=getSum2(tem,sum);
down(i,ln-lp-1,0)
ans+=getSum1(i,sum);
return ans;
}
//返回 1~n 中数字和为 sum 且字典序小于k的数的个数
ll getSum4(ll n,ll k,int sum)
{
int bit[20],i,len=0,j,t=1;
ll ans=0,pre=1;
w(k)
{
bit[len++]=k%10;
k/=10;
}
down(i,len-1,0)//枚举前缀
{
up(j,t,bit[i]-1)
{
ans+=getSum3(n,pre++,sum);
}
pre*=10;
t = 0;
}
//比如1000。小于k的有1,10,100这些是在前面统计过程中不涉及的
up(i,0,len-1)
{
if(bit[i]==0)ans++;
else break;
}
return ans;
}
//返回 k 在 1~n 中的位置
ll getSum5(ll n,ll k)
{
ll sum = 0,tem=k,ans=0,i;
while(tem)
{
sum+=(tem%10);
tem/=10;
}
up(i,1,sum-1)//计算全部和小于sum的个数
ans+=getSum2(n,i);
ans+=getSum4(n,k,sum);//和等于sum,可是字典序小于k的个数
return ans+1;//位于那些数的后一个位置
}
int main()
{
mem(dp,0);
w((scanf("%I64d%I64d",&n,&k),n+k))
{
printf("%I64d ",getSum5(n,k));
ll pre=1,presum=1,sum=1,t;
w(((t=getSum2(n,sum))<k))//统计数位和。从1開始,确定第k个位置的数位和
{
sum++;
k-=t;
}
w(1)
{
w(((t=getSum3(n,pre,sum))<k))//确定k位置的数位和后,再比較前缀,找出k位置的数
{
pre++;
presum++;
k-=t;
}
if(presum==sum) break;//数位和确定
pre*=10;
}
w(--k)pre*=10;//由于数位和确定,且前缀同样的情况还有可能是1,10,100这些类型,也要考虑
printf("%I64d\n",pre);
} return 0;
}


ZOJ2599:Graduated Lexicographical Ordering(很经典的数位DP)的更多相关文章

  1. ZOJ 2599 Graduated Lexicographical Ordering (数位DP)

    首先要吐两行槽:看到集训队论文上有这道题,由于数位DP一律写成记忆化搜索形式的强迫症,就没去看论文上的几个函数是什么……:结果被这道题虐的脑细胞死光……,最后是用随机数据对拍AC程序然后发现BUG改掉 ...

  2. ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)

    题意 定义两个数的比较方法,各位数字之和大的数大,如果数字和相等则按字典序比较两个数的大小.输入n,k,求:1.数字k的排名:2.排名为k的数. 思路 算是一类经典的统计问题的拓展吧~ 先来看第一问. ...

  3. hdu3534,个人认为很经典的树形dp

    题目大意为,求一个树的直径(最长路),以及直径的数量 朴素的dp只能找出某点开始的最长路径,但这个最长路径却不一定是树的直径,本弱先开始就想简单了,一直wa 直到我看了某位大牛的题解... 按照那位大 ...

  4. [SCOI2009] windy 数 (数位dp)

    题目 算法 应该是一道很经典的数位dp题 我们设dp[i][j]是填到第i位此时第i位的数是j的方案数 然后进行转移(代码注释) 代码 #include<iostream> #includ ...

  5. 算法复习——数位dp

    开头由于不知道讲啥依然搬讲义 对于引入的这个问题,讲义里已经很清楚了,我更喜欢用那个建树的理解···· 相当于先预处理f,然后从起点开始在树上走··记录目前已经找到了多少个满足题意的数k,如果枚举到第 ...

  6. HDU3555【数位DP】

    入门...还在学习中,先贴一发大牛博客 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意: 给一个数字n,范围在1~2^63-1,求1~ ...

  7. 数位dp相关

    经典的数位Dp是要求统计符合限制的数字的个数. 一般的形式是:求区间[n,m]满足限制f(1). f(2). f(3)等等的数字的数量是多少. 条件 f(i) 一般与数的大小无关,而与数的组成有关. ...

  8. Fzu2109 Mountain Number 数位dp

    Accept: 189    Submit: 461Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description One ...

  9. [kuangbin带你飞]专题十五 数位DP

            ID Origin Title   62 / 175 Problem A CodeForces 55D Beautiful numbers   30 / 84 Problem B HD ...

随机推荐

  1. ES6特性之模块【Modules】

    ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步加载 ...

  2. dive into python:模块的导入和搜索文件路径的配置

    1.Python中导入模块:import sys:相当于Java中的导入包.类. 比如,我们导入sys模块,使用:import sys; 2.Python中调用函数的时候,会从默认配置的库文件夹中(s ...

  3. 带有空格或tab的字符串的判断

    class test { public static void main(String[] args) { String a = " "; //带有空格的字符串 if ( a.is ...

  4. 【sqli-labs】 less65 GET -Challenge -Blind -130 queries allowed -Variation4 (GET型 挑战 盲注 只允许130次查询 变化4)

    双引号括号闭合 http://192.168.136.128/sqli-labs-master/Less-65/?id=1")%23

  5. radiobutton group

    1. 环境:VS2010 2. 分组 将radio1.radio2.radio3分为1组,radio4.radio5分为另一组: 方法:设置  radio1  的 属性:  group.tabstop ...

  6. CAD如何动态绘制带面积周长的圆?

    CAD绘制图像的过程中,画圆的情况是非常常见的,用户可以在控件视区点取任意一点做为圆心,再动态点取半径绘制圆. 主要用到函数说明: _DMxDrawX::DrawCircle 绘制一个圆.详细说明如下 ...

  7. 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

    package algorithms; /* 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. public class ListNode { int val; ListNo ...

  8. 【Hadoop】一、HDFS简介及基本概念

      当需要存储的数据集的大小超过了一台独立的物理计算机的存储能力时,就需要对数据进行分区并存储到若干台计算机上去.管理网络中跨多台计算机存储的文件系统统称为分布式文件系统(distributed fi ...

  9. JAVA基础——集合类汇总

    一.集合与数组 数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用. 集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用. ...

  10. 2019西安多校联训 Day3

    试题链接:http://www.accoders.com/contest.php?cid=1895    考试密码请私信; 特别鸣谢:zkc奆佬帮助我优化本篇题解(语言表达方面) T1 显然二分求解的 ...