ZOJ2599:Graduated Lexicographical Ordering(很经典的数位DP)
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)的更多相关文章
- ZOJ 2599 Graduated Lexicographical Ordering (数位DP)
首先要吐两行槽:看到集训队论文上有这道题,由于数位DP一律写成记忆化搜索形式的强迫症,就没去看论文上的几个函数是什么……:结果被这道题虐的脑细胞死光……,最后是用随机数据对拍AC程序然后发现BUG改掉 ...
- ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)
题意 定义两个数的比较方法,各位数字之和大的数大,如果数字和相等则按字典序比较两个数的大小.输入n,k,求:1.数字k的排名:2.排名为k的数. 思路 算是一类经典的统计问题的拓展吧~ 先来看第一问. ...
- hdu3534,个人认为很经典的树形dp
题目大意为,求一个树的直径(最长路),以及直径的数量 朴素的dp只能找出某点开始的最长路径,但这个最长路径却不一定是树的直径,本弱先开始就想简单了,一直wa 直到我看了某位大牛的题解... 按照那位大 ...
- [SCOI2009] windy 数 (数位dp)
题目 算法 应该是一道很经典的数位dp题 我们设dp[i][j]是填到第i位此时第i位的数是j的方案数 然后进行转移(代码注释) 代码 #include<iostream> #includ ...
- 算法复习——数位dp
开头由于不知道讲啥依然搬讲义 对于引入的这个问题,讲义里已经很清楚了,我更喜欢用那个建树的理解···· 相当于先预处理f,然后从起点开始在树上走··记录目前已经找到了多少个满足题意的数k,如果枚举到第 ...
- HDU3555【数位DP】
入门...还在学习中,先贴一发大牛博客 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意: 给一个数字n,范围在1~2^63-1,求1~ ...
- 数位dp相关
经典的数位Dp是要求统计符合限制的数字的个数. 一般的形式是:求区间[n,m]满足限制f(1). f(2). f(3)等等的数字的数量是多少. 条件 f(i) 一般与数的大小无关,而与数的组成有关. ...
- Fzu2109 Mountain Number 数位dp
Accept: 189 Submit: 461Time Limit: 1000 mSec Memory Limit : 32768 KB Problem Description One ...
- [kuangbin带你飞]专题十五 数位DP
ID Origin Title 62 / 175 Problem A CodeForces 55D Beautiful numbers 30 / 84 Problem B HD ...
随机推荐
- php-5.3源码编译autoconf版本不符合解法
1. 网上下载符合版本的autoconf 2. 卸载本地原本的autoconf 3. 解压autoconf 后进入目录 ./configure && make && s ...
- how to do a mass update in Laravel5 ( 在Laravel 5里面怎么做大量数据更新 )
Today, I had spent 3 hours to fix one problem, The old program has a bug, originally, when a user pr ...
- Server 2008 R2 事件查看器实现日志分析
在 windows server 2008 R2 中,可以通过点击 "开始" -> "管理工具" -> "事件查看器" ,来打开 ...
- 一个完整的网站记录(springmvc hibernate juery bootstrap)
总述 该网站为了满足测试人员自主添加测试条目,编辑更新信息和删除信息,同时同步到后台数据库的基本功能. 关键技术:oracle数据库.tomcat8.5.springMVC.Hibernate.aja ...
- c++中的类型转换--reinterpret_cast
原文链接: 浅析c++中的类型转换--reinterpret_cast转换 reinterpret_cast作用为: 允许将任何指针转换为任何其他指针类型. 也允许将任何整数类型转换为任何指针类型以 ...
- 【笔记JS/HTML/CSS】web中的HTTP协议(1)
最近都在coursera刷课,加上自己课业也忙起来了,总是忘记写学习笔记ORZ 自省ing... 在写HTML的时候,form表单需要通过HTTP协议向服务器提交.查询数据(如下图) 客户端通过HTT ...
- 并发编程学习笔记(6)----公平锁和ReentrantReadWriteLock使用及原理
(一)公平锁 1.什么是公平锁? 公平锁指的是在某个线程释放锁之后,等待的线程获取锁的策略是以请求获取锁的时间为标准的,即使先请求获取锁的线程先拿到锁. 2.在java中的实现? 在java的并发包中 ...
- 微信小程序 客服自动回复图片
产品需求是,在客服对话框里,发送特定的文字,回复我们的二维码: 小城程开发完成后,这个自动回复图片的功能就摆在了眼前.刚开始我们想到的是:在线客服功能的设置里设置好自动回复的图片,但是目前设置不支持自 ...
- share——Alpha版(内部测试版)发布
我们产品的下载二维码: 使用说明: 后期会进行更新,文件下载位置
- Python学习-while循环语句
Python 编程中 while 语句用于循环执行程序,即在某条件下,循环执行某段程序,以处理需要重复处理的相同任务.即重复性的做一件事情 语法形式如下: while 判断条件: 条件满足执行语句…… ...