ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)
题意
定义两个数的比较方法,各位数字之和大的数大,如果数字和相等则按字典序比较两个数的大小。输入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)的更多相关文章
- ZOJ 2599 Graduated Lexicographical Ordering (数位DP)
首先要吐两行槽:看到集训队论文上有这道题,由于数位DP一律写成记忆化搜索形式的强迫症,就没去看论文上的几个函数是什么……:结果被这道题虐的脑细胞死光……,最后是用随机数据对拍AC程序然后发现BUG改掉 ...
- 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. ...
- ZOJ 3494 (AC自动机+高精度数位DP)
题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3494 题目大意:给定一些被禁止的BCD码.问指定范围内不含有 ...
- zoj 3962 Seven Segment Display 数位dp
非常好的一个题,可以比赛时想到的状态太奇葩,不方便转移,就一直没能AC. 思路:dp(i, j)表示已经考虑了前i位,前i位的和为j的贡献.如果当前的选择一直是最大的选择,那么就必须从0~下一位的最大 ...
- ZOJ 3494 BCD Code (数位DP,AC自动机)
题意: 将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的.给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的 ...
- ZOJ 3494 BCD Code(AC自动机+数位DP)
BCD Code Time Limit: 5 Seconds Memory Limit: 65536 KB Binary-coded decimal (BCD) is an encoding ...
- ZOJ 3962 Seven Segment Display(数位DP)题解
题意:给一个16进制8位数,给定每个数字的贡献,问你贡献和. 思路:数位DP,想了很久用什么表示状态,看题解说用和就行,其他的都算是比较正常的数位DP. 代码: #include<iostrea ...
- ZOJ 3962 Seven Segment Display(数位DP)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3962 题目大意: 有t组数据. 给你一个n,和8位的十六进制数s ...
- 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 ...
随机推荐
- 微信小程序组件radio
表单组件radio:官方文档 Demo Code: Page({ data: { items: [ {name: 'USA', value: '美国'}, {name: 'CHN', value: ' ...
- C# 使用windows服务发送邮件
最近做了一个使用 C# 写了一个发送邮件的 windows 服务,在这里记录一下. 首先使用 Visual Studio 2015 创建一个 windows 服务项目. 然后在设计器上面右击添加安装程 ...
- [笔记]Python中模块互相调用的例子
python中模块互相调用容易出错,经常是在本地路径下工作正常,切换到其他路径来调用,就各种模块找不到了. 解决方法是通过__file__定位当前文件的真实路径,再通过sys.path.append( ...
- 定时任务之elastic-job概述
定时任务有哪些? Timer定时器 ScheduledExecutorService Spring自带的@Scheduled Quartz定时任务 当当elastic job定时任务 Quartz实现 ...
- 101. Symmetric Tree(判断二叉树是否对称)
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For ...
- 什么是Java中的原子操作( atomic operations)
1.啥是java的原子性 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行. 一个很经典的例子就是银行账户转账问题: 比如从账户A向账户B转1000元,那么 ...
- $Linux vi/vim编辑器常用命令与用法总结 (Markdown编辑版)
vi/vim是什么? Linux世界几乎所有的配置文件都是以纯文本形式存在的,而在所有的Linux发行版系统上都有vi编辑器,因此利用简单的文字编辑软件就能够轻松地修改系统的各种配置了,非常方便.vi ...
- 在Linux下不重启让配置文件修改后立即生效的办法
在linux修改配置文件后可能没有生效,比如JDK文件配置,配置后可能没有生效,为了让文件生效,如下操作可以让配置文件生效. 例如,我刚修改了“/etc/profile”或“~/.bash_profi ...
- ultraedit 查看文件
转自:https://wenda.so.com/q/1481655902726192 1 UltraEdit在打开文件的时候,会对文件类型进行检查.如果是二进制文件,会自动转为16进制显示模式.如下图 ...
- php 中处理 websocket
http://www.cnblogs.com/hustskyking/p/websocket-with-php.html 下面我画了一个图演示 client 和 server 之间建立 websock ...