bzoj 4521: [Cqoi2016]手机号码

感觉get到了一种数位dp的新姿势,加一位表示当前要填的数有没有限制(感觉以前的写法都太蠢了).

这么写有两个地方要注意:

1.每dp到一位时需要f[i][初始状态]++,相当于这位前都是前导零(这道题我把前两位填了两个10作为初始状态)。

2.因为有了1,所以初始状态后的第一位不能填0,需要特判

f[i][j][k][l][p][q][o]表示填到第几位,上上位和上位分别是什么,4,8是否出现过,三个连续的是否出现过,以及当前位是否有限制。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll pw[100];
ll f[20][11][11][2][2][2][2];
// 位数 上位 这位 4 8 出现0/1 限制0/1
ll solve(ll x)
{
	pw[0]=1;
	for(int i=1;i<=12;i++)pw[i]=pw[i-1]*10;
	int pos;
	for(int i=0;i<=12;i++)if(x>=pw[i])pos=i+1;
	memset(f,0,sizeof(f));
	f[0][10][10][0][0][0][1]=1;
	for(int i=1;i<=pos;i++)
	{
		int ks=x/pw[pos-i]%10;
	    f[i][10][10][0][0][0][0]=1;
		// 刷表
		for(int j=0;j<=10;j++)
		{
			for(int k=0;k<=10;k++)
			{
				for(int l=0;l<=1;l++)
				{
					for(int p=0;p<=1;p++)
					{
						// 这位
						for(int q=0;q<=9;q++)
						{
							if(j==10&&k==10&q==0)continue;
							bool b1=0,c1=0,d1=0;
							if(j==k&&j==q)b1=1;
							if(q==4)c1=1;
							if(q==8)d1=1;
							f[i][k][q][l|c1][p|d1][1][0]+=f[i-1][j][k][l][p][1][0];
							f[i][k][q][l|c1][p|d1][b1][0]+=f[i-1][j][k][l][p][0][0];
						}
						for(int q=0;q<=ks;q++)
						{
							if(j==10&&k==10&q==0)continue;
							bool b1=0,c1=0,d1=0;
							if(j==k&&j==q)b1=1;
							if(q==4)c1=1;
							if(q==8)d1=1;
							if(q!=ks)
							{
								f[i][k][q][l|c1][p|d1][1][0]+=f[i-1][j][k][l][p][1][1];
								f[i][k][q][l|c1][p|d1][b1][0]+=f[i-1][j][k][l][p][0][1];
							}
							else
							{
								f[i][k][q][l|c1][p|d1][1][1]+=f[i-1][j][k][l][p][1][1];
								f[i][k][q][l|c1][p|d1][b1][1]+=f[i-1][j][k][l][p][0][1];
							}
						}
					}
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<=9;i++)for(int j=0;j<=9;j++)for(int k=0;k<=1;k++)for(int l=0;l<=1;l++)for(int s=0;s<=1;s++)
	{
		if(k&&l)continue;
		ans+=f[pos][i][j][k][l][1][s];
	}
	return ans;
}
ll l,r;
int main()
{
	scanf("%lld%lld",&l,&r);
	printf("%lld\n",solve(r)-solve(l-1));
	return 0;
}

  

时间: 2024-08-04 11:41:56

bzoj 4521: [Cqoi2016]手机号码的相关文章

4521: [Cqoi2016]手机号码|数位DP

数据范围这么小..感觉暴力可过啊.. DP也是随便设计状态 F[i][j][k][s][l] 表示前i位,最后一位是j 最后一位连续出现k次(如果k已经等于3那么就一直不变)s表示4,8的出现状态 l表示前缀是否和原数的前缀相同 转移就是枚举下一位转移,也很简单.. #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio>

BZOJ 4521 CQOI 2016 手机号码 数位DP

4521: [Cqoi2016]手机号码 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 539  Solved: 325[Submit][Status][Discuss] Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不 吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码

【luogu4124】【bzoj4521】 [CQOI2016]手机号码 [数位dp]

P4124 [CQOI2016]手机号码 4521 这道题要注意卡上下界 我错了 写dfs版的更好考虑状态 写纯方程转移那个细节把我想瓜了 1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 #include<cmath> 6 #include<stack> 7 #include<algorithm> 8 using

BZOJ 4521 手机号码

SB数位dp. 我的貌似要特判9999999999的情况. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; long long l,r,dp[13][4][10][2][2][2],ret=0,bit[15]; void reset() { for (long long i=0;i<=11;i++) for (l

Bzoj4521 [Cqoi2016]手机号码

Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 557  Solved: 335 Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不 吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数 量. 工具需要检测的号码特征有两个:号码中要出现至少3个相邻的相同数字,号码中不能同 时

P4124 [CQOI2016]手机号码

链接:https://www.luogu.org/problemnew/show/P4124 题目描述 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量. 工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字:号码中不能同时出现 88 和 44 .号码必须同时包含两个特征才满

【luoguP4124 】[CQOI2016]手机号码

题目描述 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量. 工具需要检测的号码特征有两个:号码中要出现至少 3 个相邻的相同数字:号码中不能同时出现 8 和 4.号码必须同时包含两个特征才满足条件.满足条件的号码例如:13000988721.23333333333.14444101000.而

BZOJ 4520 [Cqoi2016]K远点对(KD树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4520 [题目大意] 求K远点对距离 [题解] 修改估价函数为欧式上界估价,对每个点进行dfs, 因为是无向点对,在小根堆中保留前2k个距离, 不断更新堆顶元素即可. [代码] #include <cstdio> #include <algorithm> #include <queue> using namespace std; typedef long lo

【题解】Luogu P4121 [CQOI2016]手机号码 数位DP

题目传送门 Solution 这里我采用记忆化搜索的形式实现有哪个大佬可以教教我递推板的数位dp啊 搜索的过程中记录是否曾出现8,是否曾出现4,是否曾有连续3个相同数字,搜到底返回就可以了 其实没有太多好写的,数位DP很多都是套模板 那还写来干嘛? 这道题 从$1e10$开始算! 从$1e10$开始算! 从$1e10$开始算! 这就意味着我们需要特殊处理最高位,而不是像普通的数位DP那样直接把最高位也丢进搜索里搜. 惨痛的70分教训大概只有我是这么蠢了嘤嘤嘤 Code #include <cst