题意

定义两个数的比较方法,各位数字之和大的数大,如果数字和相等则按字典序比较两个数的大小。输入n,k,求:1.数字k的排名;2.排名为k的数。

思路

算是一类经典的统计问题的拓展吧~

先来看第一问。求数字K的排名,变相得看就是求[1,N]中小于K的数的个数,数位DP统计下即可(记忆化搜索方式,dfs()过程):pos表示处理的位置;dig_sum表示当前枚举的数位和;隐藏的全局比较对象k_sum是K的数位和;start表示枚举数位开始的位置,即第一个非零位,便于按位DP时比较字典序;flag表示字典序是否小于K,-1表示小于,0表示未决定,1表示大于,比较过程在按位DP过程中即完成(详见next_flag计算);limit表示当前数位的数字是否达上限。

然后来看第二问。如果是像此题这样和数大小不成线性关系的排名规则,尝试正着求排名可能会比较麻烦。所以一般处理这种排序问题的方法是用某种方法枚举符合规则的数,然后判断这个数是不是排第K(求一个数的排名用第一问的方法)。一般枚举数可以二分,blablabla……当然这道题二分不行,因为字典序和数位和规则都不具备单调性。而朴素枚举显然不行,在这道题中,大家普遍的方法是枚举前缀~具体一点儿就是:枚举第一位是几,再枚举第二位是几,直到确定这个数字。枚举每一位时都是从0~9(除了第一位不能是0),这样就保证了枚举的顺序符合字典序递增的顺序。而我们也可以通过枚举数位和来算出第K个数的数位和(见代码)。总结一下第二问的思路就是:先算出第K个数的数位和k_dig_num,然后枚举前缀,根据当前前缀、数位和为k_dig_num下的数的个数,由于字典序,所以前面的数一定小于第K个数,这样下去知道当前枚举到的数是第K个数。

代码

[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, end) for (int i = begin; i <= end; i ++)
using namespace std;

typedef unsigned long long LL;
typedef vector <int> VI;

int k_sum, k_dig_sum;
VI num, k_num;
LL dp[25][200][25][5], fsum[25][200];
LL res1, res2;
int sum;

LL dfs(int pos, int dig_sum, int start, int flag, bool limit){
if (pos == -1){
if (dig_sum == 0) return 0;
if (dig_sum > k_sum) return 0;
else if (dig_sum == k_sum){
if (!flag)
if ((int)k_num.size()-1-(start-pos) >= 0)
return 1;
else
return 0;
else{
return (flag==-1)?1:0;
}
}
else if (dig_sum < k_sum) return 1;
}
if (!limit && ~dp[pos][dig_sum][start][flag]) return dp[pos][dig_sum][start][flag];
int end = limit?num[pos]:9; LL res = 0;
REP(i, 0, end){
bool st = (start == pos && i == 0);
int next_flag;
if (flag != 0) next_flag = flag;
else{
if (st) next_flag = 0;
else {
if ((int)k_num.size()-1-(start-pos) < 0) next_flag = 1;
else{
if (i < k_num[(int)k_num.size()-1-(start-pos)]) next_flag = -1;
else if (i == k_num[(int)k_num.size()-1-(start-pos)]) next_flag = 0;
else next_flag = 1;
}
}
}
res += dfs(pos-1, dig_sum+i, st?start-1:start, next_flag, limit&&(i==end));
}
return limit?res:dp[pos][dig_sum][start][flag]=res;
}
LL find_dig_sum(int pos, int dig_sum, bool limit){
if (pos == -1) return dig_sum == sum;
if (!limit && ~fsum[pos][dig_sum]) return fsum[pos][dig_sum];
int end = limit?num[pos]:9;
LL res = 0;
REP(i, 0, end){
res += find_dig_sum(pos-1, dig_sum+i, limit&&(i==end));
}
return limit?res:fsum[pos][dig_sum]=res;
}
inline void cal(LL x){
num.clear();
while(x){
num.push_back(x%10);
x /= 10;
}
}
inline void cal_k(LL k){
k_num.clear();
k_sum = 0;
while(k){
k_sum += k%10;
k_num.push_back(k%10);
k /= 10;
}
}
LL solve_pos(LL n, LL k){
if (k == 0) return 0;
cal_k(k);
cal(n);
MEM(dp, -1);
return dfs(num.size()-1, 0, num.size()-1, 0, 1) + 1;
}
LL solve_rank(LL n, LL k){
LL sum_num = 0;
LL dig_sum_rank = k;
for (sum = 1; sum <= 180; sum ++){
MEM(fsum, -1);
sum_num += find_dig_sum(num.size()-1, 0, 1);
if (sum_num >= k){
k_dig_sum = sum;
if (sum > 1){
dig_sum_rank -= sum_num - find_dig_sum(num.size()-1, 0, 1);
}
break;
}
}
LL testnum = 0, sumranks = 0;
while(solve_pos(n, testnum) != k){
testnum *= 10;
LL ranks = 0;
REP(i, 0, 9){
ranks = 0;
if (testnum == 0 && i == 0) continue;
LL begin = testnum+i;
LL offset = 1;
while(begin<=n){
cal(min(n, begin+offset-1));
ranks += find_dig_sum(num.size()-1, 0, 1);
cal(begin-1);
ranks -= find_dig_sum(num.size()-1, 0, 1);
begin *= 10;
offset *= 10;
}
if (sumranks + ranks >= dig_sum_rank){
testnum = testnum + i;
break;
}
sumranks += ranks;
}
}
return testnum;
}
LL n, k;
int main(){
while(scanf("%lld %lld", &n, &k), n+k){
res1 = res2 = -1;
res1 = solve_pos(n, k);
res2 = solve_rank(n, k);
printf("%lld %lld\n", res1, res2);
}
return 0;
}
[/cpp]

ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)的更多相关文章

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

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

  2. 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. ...

  3. ZOJ 3494 (AC自动机+高精度数位DP)

    题目链接:  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3494 题目大意:给定一些被禁止的BCD码.问指定范围内不含有 ...

  4. zoj 3962 Seven Segment Display 数位dp

    非常好的一个题,可以比赛时想到的状态太奇葩,不方便转移,就一直没能AC. 思路:dp(i, j)表示已经考虑了前i位,前i位的和为j的贡献.如果当前的选择一直是最大的选择,那么就必须从0~下一位的最大 ...

  5. ZOJ 3494 BCD Code (数位DP,AC自动机)

    题意: 将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的.给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的 ...

  6. ZOJ 3494 BCD Code(AC自动机+数位DP)

    BCD Code Time Limit: 5 Seconds      Memory Limit: 65536 KB Binary-coded decimal (BCD) is an encoding ...

  7. ZOJ 3962 Seven Segment Display(数位DP)题解

    题意:给一个16进制8位数,给定每个数字的贡献,问你贡献和. 思路:数位DP,想了很久用什么表示状态,看题解说用和就行,其他的都算是比较正常的数位DP. 代码: #include<iostrea ...

  8. ZOJ 3962 Seven Segment Display(数位DP)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3962 题目大意: 有t组数据. 给你一个n,和8位的十六进制数s ...

  9. ZOJ 3962 E.Seven Segment Display / The 14th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple E.数位dp

    Seven Segment Display Time Limit: 1 Second      Memory Limit: 65536 KB A seven segment display, or s ...

随机推荐

  1. python16_day23【cmdb前端】

    一.cmdb前端 https://github.com/willianflasky/growup/tree/master/s16/homework/day23_cmdb_web/s16MadKing ...

  2. XDU 1130 (快速幂)

    题目大意:原题链接 解题思路:sum((p+i*d)*C(n,i))=sum(p*C(n,i)+d*i*C(n,i)) ; 又因为i*C(n,i)=n*C(n-1,i-1) 所以sum((p+i*d) ...

  3. __new__方法以及TypeError: object() takes no parameters的处理

    一些python书或博客将类中的__init__方法称为构造函数,而实际上这种说法是不严格的,因为创建实例的方法是__new__,实例初始化的方法是__init__.__new__方法会返回一个实例, ...

  4. cdoj1580 简单图论问题

    地址:http://acm.uestc.edu.cn/#/problem/show/1580 题目: 简单图论问题 Time Limit: 3000/1000MS (Java/Others)     ...

  5. testng失败截图,注解方式调用。

    今天一整天都在研究testng失败截图的方法,参考网上的前辈们的资料,加上自己的理解,终于搞出来了. package com.dengnapianhuahai; /** * 自定义注释 * */ im ...

  6. ipython安装(python3.6.1)

    之前一直是在Python2.7环境下装的ipython,今天想在Python3环境下安装,主要是为了方便测试,按tab键可以补全,显示导入模块的方法. 1.首先安装Python3.6.1 wget h ...

  7. vux在ISO中异常 this.$vux.confirm.show

    在按钮事件中调用this.$vux.confirm.show,并且启用按钮的show-loading属性 安卓正常,ios中弹窗无法显示 经过排查,iso中设置按钮的loading后,要用异步setT ...

  8. HBuilder 详细使用方法 -------------参考 :http://www.runoob.com/w3cnote/hbuilder-intro.html

    HBuilder是DCloud(数字天堂)推出的一款支持HTML5的Web开发IDE.HBuilder的编写用到了Java.C.Web和Ruby.HBuilder本身主体是由Java编写,它基于Ecl ...

  9. Python学习笔记之Centos6.9安装Python3.6

    0x00 注意 如果本机安装了python2,尽量不要管他,使用python3运行python脚本就好,因为可能有程序依赖目前的python2环境, 比如yum!!!!! 不要动现有的python2环 ...

  10. 20145335郝昊《Java程序设计》课程总结

    20145335郝昊<Java程序设计>课程总结 1.读书笔记汇总 第一周学习总结:http://www.cnblogs.com/20145335hh/p/5244638.html 第二周 ...