【[CQOI2016]手机号码】
递推版的数位dp
绝对的暴力美学
我们设\(dp[l][i][j][0/1][0/1][0/1]\)表示到了第\(l\)位,这一位上选择的数是\(i\),\(l-1\)位选择的数是\(j\),第一个\(0/1\)代表\(4\)没有/有出现过,第二个\(0/1\)代表\(8\)没有/有出现过,第三个\(0/1\)代表连续三位没有/有出现过
于是转移很简单了
但是卡位实在是鬼畜
我卡位的方式有些鬼畜,所以细节非常的多
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 16
#define LL long long
LL L,R;
int a[maxn],num;
LL dp[maxn][11][11][2][2][2];
//位数,这一位上的数,上一位的数,0/1表示4/8有/没有出现,0/1表示有/没有连续三位
inline LL slove(LL x)
{
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
num=0;
while(x)
{
a[++num]=x%10;
x/=10;
}//分解数位
a[num+1]=-11,a[num+2]=11;
for(re int i=0;i<=9;i++)
for(re int j=0;j<=9;j++)
for(re int k=0;k<=9;k++)
{
int opt_4=0,opt_8=0;
if(i==4||j==4||k==4) opt_4=1;
if(i==8||j==8||k==8) opt_8=1;
if(i==j&&j==k) dp[3][i][j][opt_4][opt_8][1]+=1;
else dp[3][i][j][opt_4][opt_8][0]+=1;
}//先初始化dp[3]之后再往下推
for(re int l=3;l<num;l++)//刷表转移
for(re int i=0;i<=9;i++)
for(re int j=0;j<=9;j++)
for(re int k=0;k<=9;k++)
for(re int o4=0;o4<=1;o4++)
for(re int o8=0;o8<=1;o8++)
for(re int o=0;o<=1;o++)
dp[l+1][i][j][(i==4)||o4][(i==8)||o8][((i==j)&&(j==k))||o]+=dp[l][j][k][o4][o8][o];
//方程其实挺简单的,就是看看这一位有没有4/8/连续三位就好了
LL ans=0;
for(re int i=3;i<num;i++)//从位数小于给定数的开始
for(re int j=1;j<=9;j++)
for(re int k=0;k<=9;k++)
ans+=dp[i][j][k][0][0][1]+dp[i][j][k][1][0][1]+dp[i][j][k][0][1][1];
for(re int i=1;i<a[num];i++)//位数和给定数相等,但是首位比较小
for(re int j=0;j<=9;j++)
ans+=dp[num][i][j][0][0][1]+dp[num][i][j][1][0][1]+dp[num][i][j][0][1][1];
int o4=0,o8=0,o=0;// 4/8/连续三位有没有出现过
if(a[num]==4) o4=1;
if(a[num]==8) o8=1;
for(re int l=num-1;l>=3;l--)//卡位,这里保证从[l+1,num]和给定数是完全相等的
{
if(o4&&o8) break;
for(re int i=0;i<a[l];i++)
{
int flag=o;
if(i==a[l+1]&&a[l+1]==a[l+2]) o=1;//由于我们选择这一位有可能会导致和上面的两位重复,于是这里需要判断一下,如果有那么就o=1接下来就算没有选出连续三位也可以了
for(re int j=0;j<=9;j++)
{
int cnt=o;
if(i==j&&i==a[l+1]) o=1;
//和上面两位重合的情况
if(o)
{
if(o4&&!o8) ans+=dp[l][i][j][1][0][1]+dp[l][i][j][0][0][1]
+dp[l][i][j][0][0][0]+dp[l][i][j][1][0][0];
if(!o4)
{
if(!o8) ans+=dp[l][i][j][0][0][1]+dp[l][i][j][0][0][0]
+dp[l][i][j][1][0][1]+dp[l][i][j][1][0][0]
+dp[l][i][j][0][1][1]+dp[l][i][j][0][1][0];
if(o8) ans+=dp[l][i][j][0][1][1]+dp[l][i][j][0][1][0]
+dp[l][i][j][0][0][1]+dp[l][i][j][0][0][0];
}
}
if(!o)
{
if(o4&&!o8) ans+=dp[l][i][j][1][0][1]+dp[l][i][j][0][0][1];
if(!o4)
{
if(!o8) ans+=dp[l][i][j][0][0][1]+dp[l][i][j][1][0][1]+dp[l][i][j][0][1][1];
if(o8) ans+=dp[l][i][j][0][1][1]+dp[l][i][j][0][0][1];
}
}
o=cnt;
}
o=flag;//将o还原回来
}
o4=o4||(a[l]==4);
o8=o8||(a[l]==8);
o=o||((a[l]==a[l+1])&&(a[l+1]==a[l+2]));
//判断有没有连续三位4/8出现过
}
if(o4&&o8) return ans;
for(re int i=0;i<=9;i++)
for(re int j=0;j<=9;j++)//卡最后两位
{
if(i*10+j>a[2]*10+a[1]) continue;//不能超过给定数的最后两位
if(o4&&o8) continue;
if(i==4||j==4)
if(o8) continue;//有4就不能有8
if(i==8||j==8)
if(o4) continue;//有8就不能有4
if(i==4&&j==8) continue;
if(i==8&&j==4) continue;//更不可能同时出现
if(o||(i==j&&j==a[3])||(i==a[3]&&a[3]==a[4])) ans++;
//最后两位仍有可能和前面构成三位连续
}
return ans;
}
int main()
{
scanf("%lld%lld",&L,&R);
printf("%lld\n",slove(R)-slove(L-1));
return 0;
}
【[CQOI2016]手机号码】的更多相关文章
- 4521: [Cqoi2016]手机号码
4521: [Cqoi2016]手机号码 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 1030 Solved: 609 [Submit][Statu ...
- [BZOJ4521][CQOI2016]手机号码(数位DP)
4521: [Cqoi2016]手机号码 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 875 Solved: 507[Submit][Status ...
- [Bzoj4521][Cqoi2016]手机号码(数位dp)
4521: [Cqoi2016]手机号码 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 870 Solved: 505[Submit][Status ...
- [CQOI2016]手机号码 数位DP
[CQOI2016]手机号码 用来数位DP入门,数位DP把当前是否需要限制取数范围(是否正在贴着临界值跑,即下面的limited)和一切需要满足的条件全部塞进记忆化搜索参数里面就好了,具体情况转移便好 ...
- P4124 [CQOI2016]手机号码
P4124 [CQOI2016]手机号码 题解 数位DP DFS 虽然套路,但还是恶心到找不到锅在哪里 注意这个 然后你就发现其实这样就不用记录前导0了 锅在这个鬼地方QAQ 代码 #inclu ...
- [Luogu P4124] [CQOI2016]手机号码 (数位DP)
题面 传送门:洛咕 Solution 感谢神仙@lizbaka的教学 这题是数位DP的非常非常模板的题目,只是状态有点多 . 这题我使用记忆化搜索实现的 中国有句古话说的好,有多少个要求就设多少个状态 ...
- BZOJ4521: [Cqoi2016]手机号码
Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不 吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单 ...
- BZOJ4521 Cqoi2016 手机号码 【数位DP】
Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出 ...
- 【洛谷P4124】[CQOI2016]手机号码
手机号码 数位DP模板题 记忆化搜索: #include<iostream> #include<cstring> #include<cstdio> using na ...
- [BZOJ4521][Cqoi2016]手机号码 (数位dp)
题目描述 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售.为了便于前 ...
随机推荐
- checkebox 全选 ,子复选框单个全部选择后,全选框也会被选择
<script> //点击全选,子复选框被选中 function demo(){ var allcheck=document.getElementById("allcheck&q ...
- 验证是否是正整数,是否是mail,是否是正确的身份证
/// <summary> /// 通用验证类 /// </summary> class DataValidate { /// <summary> /// 验证正整 ...
- RabbitMQ---3、c#实现
1.EasyNetQ组件的使用 EasyNetQ组件的使用方式比较简单,跟很多组件都类似,例如:建立连接,进行操作做等等,对于EasyNetQ组件也是如此.(mq的升级,用于简化rabbitmq应用代 ...
- Mongodb操作之查询(循序渐进对比SQL语句)(转http://www.tuicool.com/articles/UzQj6rF)
工具推荐:Robomongo,可自行百度寻找下载源,个人比较推荐这个工具,相比较mongoVUE则更加灵活. 集合简单查询方法 mongodb语法:db.collection.find() //co ...
- 小程序异步处理demo计时器setInterval()
实现一个计时器/秒 其实就是要求对某字段每秒执行一次更新 这里用到了官方给的定时器 官方API 每秒刷新一次,所以用setInterval()方法 下面给出关键代码: 由于无关代码过多,这里尽可能贴出 ...
- Window ssh免密登录到远程Linux服务器
SSH采用的是”非对称密钥系统”,即耳熟能详的公钥私钥加密系统. 1. 基于口令的安全验证 这种方式使用用户名密码进行联机登录,一般情况下我们使用的都是这种方式.整个过程大致如下: (1)客户端发起连 ...
- float失效的情况
前言:在最近的笔试中,两次碰到类似的问题,什么情况下float会失效?我目前知道的有2种: 1)display:none: 2)position:absolute.fixed. (1)display: ...
- HTML5之新增的元素和废除的元素 (声明:内容节选自《HTML 5从入门到精通》)
新增结构元素: section元素 section元素定义文档或应用程序中的一个区段,比如章节.页眉.页脚或文档中的其他部分.它可以与h1,h2,h3,h4,h5,h6元素结合起来使用,标示文档结构. ...
- 简易搭建git仓库、关联远程和本地仓库方法。克隆仓库方法。同一台电脑上创建两个git ssh key方法。
一,在github上建仓库 react-js-antd-demo: 二:将远程仓库与本地仓库关联 git remote add origin git@github.com:begin256/react ...
- npm 更新至最新版本
有时npm版本低了,一些操作有问题,要更新成最新. 官网中: https://www.npmjs.com/get-npm 先查看对应的 node 版本 和 npm版本 Check that ...