poj3208 Apocalypse Someday[数位DP]
数位中出现至少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]的更多相关文章
- 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的数. ...
- POJ 3689 Apocalypse Someday [数位DP]
Apocalypse Someday Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 1807 Accepted: 87 ...
- POJ3208 Apocalypse Someday(二分 数位DP)
数位DP加二分 //数位dp,dfs记忆化搜索 #include<iostream> #include<cstdio> #include<cstring> usin ...
- POJ3208 Apocalypse Someday
题意 Language:Default Apocalypse Someday Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 2 ...
- POJ-3208 Apocalypse Someday (数位DP)
只要某数字的十进制表示中有三个6相邻,则该数字为魔鬼数,求第X小的魔鬼数\(X\le 5e7\) 这一类题目可以先用DP进行预处理,再基于拼凑思想,用"试填法"求出最终的答案 \( ...
- POJ 3208-Apocalypse Someday(数位dp)
题意:给定n,输出第n大包含666的数字. 分析:dp[i][j][k][l]表示 长度为i,当前位是否是6,前一位是否6,是否已经包含666,表示的数量,再用二分找出第n大的这样的数字. #incl ...
- 数位DP专题
这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪. http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html in ...
- 开坑数位dp
[背景] 在10月3日的dp专练中,压轴的第6题是一道数位dp,于是各种懵逼. 为了填上这个留存已久的坑,蒟蒻chty只能开坑数位dp了. [例题一][HDU2089]不要62 题目大意:给你一个区间 ...
- 数位dp真·浅谈 By cellur925
预警:由于是从$Vergil$学长那里和$Mathison$大神那里学来的,所以清一色记忆化搜索!qwq 巨佬的数位dp讲解(未来的咕咕日报头条): https://www.luogu.org/blo ...
随机推荐
- 【BZOJ4766】文艺计算姬
让你求一个两边各有n和m个点的完全二分图有多少个生成树. 这是一道比较经典的利用prufer序列结论求解答案的计数题. 大致思路考虑一张二分图求解prufer序列,由于prufer序列求解时最后剩下的 ...
- TCP协议基础(一)
TCP为应用层提供的服务 提供进程和进程之间的通信 答: 怎么区分服务目的主机上的哪个进程呢? 通过提供端口号(well-known port), 如Telnet 23,DNS 53, HTTP 80 ...
- 【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念
linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10.04 开发平台:S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- SQL语句规范
SQLStructure Query Language,结构化查询语言 1.规范(1)mysql对于SQL语句不区分大小写,SQL语句关键字尽量大写 show databases;show DataB ...
- 01:gitbook使用
1.1 gitbook介绍 1.gitbook说明 GitBook 使用的markdown语法 在此基础上做了一些 写作便利性的加强 Markdown 是一种轻量级的「标记语言」,优点在于 专注你的文 ...
- Android Monkey测试入门<1>
第一步:搭建环境:主要是安装和搭建java和sdk环境,说白了,对我们安卓开发来说,只要搭建好了Android开发环境,Monkey测试环境基本就是OK的了.可以参考:http://www.cnblo ...
- EM 算法(二)-KMeans
KMeans 算法太过简单,不再赘述 本文尝试用 EM 算法解释 KMeans,而事实上 KMeans 算是 EM 的一个特例 EM 算法是包含隐变量的参数估计模型,那对应到 KMeans 上,隐变量 ...
- The minimal unique substring CodeForces - 1159D (构造)
核心观察是形如01,001,0001,...的串循环时, $n$每增长1, $k$就增长1. #include <iostream> #include <sstream> #i ...
- php运行结果设置无缓存
修改配置php.ini vim /usr/local/php/lib/php.ini opcache.enable= 重启php服务 service php-fpm restart done! 参考地 ...
- django自带登录认证与登录自动跳转
# 导入django自带模块 from django.contrib.auth import authenticate, login, logout # 使用authenticate进行认证,使用lo ...