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 export-from用法

    语法: export * from "foo"; export {foo} from "foo"; export {foo, bar} from "f ...

  2. 梦想CAD控件网页版线型

    增加线型 主要用到函数说明: _DMxDrawX::AddLinetype 增加一个线型定义.详细说明如下: 参数 说明 BSTR pszName 线型名 BSTR pszLineDefine 线定义 ...

  3. 【原】SMTP发送邮件

    1.下载class.phpmailer.php和class.smtp.php至公共库 2.编写发邮件的公共函数 function sendMail($param) { $config = C('THI ...

  4. 并查集(Union Find)的基本实现

    概念 并查集是一种树形的数据结构,用来处理一些不交集的合并及查询问题.主要有两个操作: find:确定元素属于哪一个子集. union:将两个子集合并成同一个集合. 所以并查集能够解决网络中节点的连通 ...

  5. TWaver矢量小试——Android演进路线图

    还有半个多月就到春节了,年底相信很多公司都会进行年度总结以及公司发展状况总结,在这过程中难免会用到RoadMap,在这我们也使用TWaver的矢量部分绘制一个Android系统的发展历程.先看效果:什 ...

  6. MONO Design创建电信3D机房

    前面我们简单介绍了下一分钟创建3D机房,实则mono Design的功能远远不止这些,试想一下,如果我们花上10分钟来创建一个电信机房,那么MONO design又会给我们带来什么样的惊喜呢? 我们从 ...

  7. Python学习-变量

    什么是变量? 概念:变量就是会变化的量,主要是“变”与“量”二字.变即是“变化”. 特点:与其他编程语言相同,变量是最基本的存储单位,是用来存放数据的容器.可以引用一个具体的数值,进而直接去改变这个引 ...

  8. VM 安装ubuntu16.04简易方法

    在已经安装好VM10虚拟机后 首先文件—>新建虚拟机—>典型(标准)  选择稍后安装操作系统,后续要使用的是已经下载好的ubuntu16.04镜像  选择操作系统是linux ,版本是ub ...

  9. ubuntu jdk和tomcat配置

    先查看linux的版通过file /sbin/init命令,下载对应版本的jdk. 我的ubuntu是64位的(桌面系统),所以下载的是jdk-7u71-linux-x64.tar.gz 在home的 ...

  10. IDLE in Python (Ubuntu)

    To lauch IDLE in the Current Woking Directory >>> usr/bin/idle3 Alt + n  # next command Alt ...