数位中出现至少3个连续的'6'的数字(称魔鬼数),询问满足要求的排名k的数。


经典题型。采用试填法。

递推做法:预处理出$i$位数字中满足要求的数(下记为'魔鬼数')。对每一位都从0到9试一遍,然而卡在了试填时试到6这个数时该怎么办,不太会做。然后才知道可以记录填到目前的上一位已有多少个连续的6,这样可以配合当前的计算出来。

所以就有这个$f[i][0]=9*(f[i-1][0]+f[i-1][1]+f[i-1][2]),f[i][1]=f[i-1][0],f[i][2]=f[i-1][1],f[i][3]=10*f[i-1][3]+f[i-1][2]$

其中0表示开头有0个6,1表示开头有一个6,2同理,3表示当前小于等于i位的魔鬼数的数量(也就相当于把各位数的魔鬼数累积起来)

注意是算入前导0的。比如有004566674,因为试填时他也算一种。而开头不必担心,看代码第1次循环就会发现考虑0的时候恰好把小于i位的魔鬼数数量都减掉了,说不太清楚,还是自己想想吧。

用k以辅助记录当前填过的末尾连续的6数量,用于处理填6时计算,每填一个6就多算一下。k到3时表示我已经诞生连续3个的6了,所以后面所有的填法都合法,这是要再单独特殊处理,看code。

其他就是常规的试填。

说两点细节:

注意边界处理 ,考虑预处理DP值的边界,尤其是f[0],f[1]等;

试填也要考虑边界。比如开始时。结束时(最后一位),这直接和f[0][0]的初始化产生了联系。所以两个边界都要看一下检查正确性。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
ll f[][],rk,cnt;
int T;
inline void preprocess(){
f[][]=,f[][]=;f[][]=;//←注意边界处理 ,DP和试填都要检验一下
for(register int i=;i<=;++i)f[i][]=*(f[i-][]+f[i-][]+f[i-][]),f[i][]=f[i-][],f[i][]=f[i-][],f[i][]=*f[i-][]+f[i-][];
} int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout);
read(T);preprocess();while(T--){
read(rk);int x;for(x=;x<=;++x)if(rk<=f[x][])break;
for(register int i=x,k=;i;--i){
for(register int j=;j<=;++j){
cnt=f[i-][];
if(k==)cnt+=f[i-][]+f[i-][]+f[i-][];
else if(j==)for(register int l=-k;l<;++l)cnt+=f[i-][l];
if(rk<=cnt){
if(k<){if(j==)++k;else k=;}
printf("%d",j);break;
}
else rk-=cnt;
}
}
puts("");
}
return ;
}

然后讲一下记搜的,可以套模板。求第k大的数,可以转化为二分枚举区间[1,n]的右边界n,看[1,n]的满足要求的数,由于其随区间变大而单调增,所以是二分。通过不断调整,可以找到最小n使得区间[1,n]恰有k个满足要求的数。这个因为是套模板,所以很好写。但是二分的话会让复杂度多一个log。。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
int T;
ll L,R,f[][][][],b[],aim;
ll dp(int len,bool las,bool las2,bool fil,bool limit){
if(!len)return fil;
if(!limit&&~f[len][las][las2][fil])return f[len][las][las2][fil];
ll cnt=;int num=limit?b[len]:;
for(register int i=;i<=num;++i)cnt+=dp(len-,i==,las,fil||(las&&las2&&i==),limit&&i==num);
return limit?cnt:f[len][las][las2][fil]=cnt;
}
inline ll solve(ll x){
int k=;while(x)b[++k]=x%,x/=;
return dp(k,,,,);
} int main(){//freopen("tmp.txt","r",stdin);//freopen("test.out","w",stdout)£»
memset(f,-,sizeof f);
read(T);while(T--){
read(aim);L=,R=1e10;ll mid;
while(L<R){
mid=L+R>>;
if(solve(mid)<aim)L=mid+;
else R=mid;
}
printf("%lld\n",L);
}
return ;
}

poj3208 Apocalypse Someday[数位DP]的更多相关文章

  1. poj3208 Apocalypse Someday 数位dp+二分 求第K(K <= 5*107)个有连续3个6的数。

    /** 题目:poj3208 Apocalypse Someday 链接:http://poj.org/problem?id=3208 题意:求第K(K <= 5*107)个有连续3个6的数. ...

  2. POJ 3689 Apocalypse Someday [数位DP]

    Apocalypse Someday Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 1807   Accepted: 87 ...

  3. POJ3208 Apocalypse Someday(二分 数位DP)

    数位DP加二分 //数位dp,dfs记忆化搜索 #include<iostream> #include<cstdio> #include<cstring> usin ...

  4. POJ3208 Apocalypse Someday

    题意 Language:Default Apocalypse Someday Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 2 ...

  5. POJ-3208 Apocalypse Someday (数位DP)

    只要某数字的十进制表示中有三个6相邻,则该数字为魔鬼数,求第X小的魔鬼数\(X\le 5e7\) 这一类题目可以先用DP进行预处理,再基于拼凑思想,用"试填法"求出最终的答案 \( ...

  6. POJ 3208-Apocalypse Someday(数位dp)

    题意:给定n,输出第n大包含666的数字. 分析:dp[i][j][k][l]表示 长度为i,当前位是否是6,前一位是否6,是否已经包含666,表示的数量,再用二分找出第n大的这样的数字. #incl ...

  7. 数位DP专题

    这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪. http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html in ...

  8. 开坑数位dp

    [背景] 在10月3日的dp专练中,压轴的第6题是一道数位dp,于是各种懵逼. 为了填上这个留存已久的坑,蒟蒻chty只能开坑数位dp了. [例题一][HDU2089]不要62 题目大意:给你一个区间 ...

  9. 数位dp真·浅谈 By cellur925

    预警:由于是从$Vergil$学长那里和$Mathison$大神那里学来的,所以清一色记忆化搜索!qwq 巨佬的数位dp讲解(未来的咕咕日报头条): https://www.luogu.org/blo ...

随机推荐

  1. .net代码混淆 .NET Reactor 研究 脚本一键混淆一键发布

    .net代码混淆 .NET Reactor 研究 为什么要混淆? .net比较适合快速开发桌面型应用,但缺点是发布出来的文件是可以反编译的,有时候为了客户的安全,我们的代码或者我们的逻辑不想让别人知道 ...

  2. SQLSERVER去除某一列的重复值并显示所有数据\DISTINCT去重\ISNULL()求SUM()\NOT EXISTS的使用

    进入正题,准备我们的测试数据 1.我们要筛选的数据为去除 GX 列的重复项 并将所有数据展示出来,如图所示: ' 2.这种情况下我们是不可以使用DISTINCT来去重的,我们可以来尝试一下: 首先,单 ...

  3. python-Web-django-邮箱登陆

    setting: # 发送邮箱 EMAIL_HOST = 'smtp.163.com' EMAIL_PORT = 465 EMAIL_HOST_USER = '666666@163.com' #EMA ...

  4. Ubuntu 16.04安装anaconda3

    首先下载anaconda3镜像,清华大学开源软件镜像站下载地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 因为实验需要用到tenso ...

  5. 【转贴】优秀的龙芯Docker虚拟机解决方案

    优秀的龙芯Docker虚拟机解决方案 http://www.loongson.cn/m/view.php?aid=836 时间:2019-05-23 15:03  来源:未知   作者:龙芯中科    ...

  6. (5.3)mysql高可用系列——mysql复制(理论篇)【续写中】

    关键词:mysql主从复制,mysql复制,MGR,mysql并行复制 目录 [1]mysql支持的复制类型 [2]mysql的主从4种同步方式介绍 (1)异步 (2)同步 (3)5.6 半同步 (4 ...

  7. NumPy进阶

    数组算术 任何两个等尺寸数组之间的算术操作都应用了逐元素操作的方式. arr1 = np.array([[1,2,3],[4,5,6]]) arr2 = np.array([[4,2,1],[7,2, ...

  8. redis缓存穿透00

    缓存穿透 缓存穿透,是指查询一个数据库一定不存在的数据.正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存.如果数据库 ...

  9. 学习python基础规则

    前面应该是记流水账的方式,毕竟学习的内容不多无法产出什么有效的内容. 这两天从开始下载Python开始学习,一路顺畅冒的问题,直到开始学习python的游戏规则,严格缩进.注释及‘’的使用等感觉还不错 ...

  10. The Party and Sweets CodeForces - 1159C (拓排)

    优化连边然后拓排. #include <iostream> #include <sstream> #include <algorithm> #include < ...