手机号码

手机号码

题意

\(11\) 位手机号,求 \([L,R]\) 之间所有满足以下条件的号码个数:

  1. \(4\)\(8\) 不同时出现。

  2. 存在三个连续的数。

解析

拿来当数位 DP 板子(记搜版),记搜好用。

\([L,R]\) 的个数,我们用前缀和,求出 \([1,R]\)\([1,L-1]\) 的个数,把限制条件变成一个。

dp 数组多开几维,把能记的状态都记上,转移按位枚举 \([0,9]\),并且注意限制条件。

数位 DP 有一个很常见的限制条件是某一位是否达到最大数,如果达到了,那后面的数不能无限制的填,否则 \([0,9]\) 都可以。

然后你会发现所有数位 DP好像长得差不多。

#include<cstdio>
#include<cstring>
#define LL long long
LL l,r,f[11][11][11][2][2][2][2];
int n[12];
inline LL dfs(int p,int a,int b,bool c,bool t,bool _4,bool _8)
{
//	p:now
//	a:p+1
//	b:p+2
//	c:whether reach the limit?
//	t:is there "three"
	if(_4&&_8) return 0;//this is top priority
	if(p<=0) return t;//pay attention the order
	if(f[p][a][b][c][t][_4][_8]!=-1) return f[p][a][b][c][t][_4][_8];
	LL res=0;int top=(c?n[p]:9);
	for(int i=0;i<=top;i++)
		res+=dfs(p-1,i,a,(c&&i==top),t||(i==a&&i==b),_4||i==4,_8||i==8);
	return f[p][a][b][c][t][_4][_8]=res;
}
LL work(LL x)
{
	if(x<1e10) return 0;//if smaller than 1e10,none.
	int len=0;
	while(x) n[++len]=x%10,x/=10;
	LL res=0;
	memset(f,-1,sizeof(f));
	for(int i=1;i<=n[len];i++)
		res+=dfs(10,i,0,i==n[len],0,i==4,i==8);
	return res;
}
int main()
{
	scanf("%lld%lld",&l,&r);
	printf("%lld\n",work(r)-work(l-1));
	return 0;
}
posted @ 2024-06-21 20:54  ppllxx_9G  阅读(38)  评论(0编辑  收藏  举报