【CQOI 2016】手机号码

传送门


Problem

求出 [    l , r    ] [\;l,r\;] [l,r] 中,满足:数的长度为 11 11 11;号码中要出现至少 3 3 3 个相邻的相同数字;号码中不能同时出现 8 8 8 4 4 4 的数的个数。

1 0 10 ≤ l ≤ r < 1 0 11 10^{10} ≤l≤r<10^{11} 1010lr<1011


Solution

算是一道比较简单的数位DP题。

第一个条件,根据数据范围,只需注意一点小细节就行了。

第二个,只用记录下上一个数上一个数的上一个数,在DP的时候判断一下就行了。

第三个,用两个 b o o l bool bool 记录是否出现 4 4 4 8 8 8,在最后判断一下就可以了

还有就是要注意,由于数的长度是 11 11 11,所以第一位不能填 0 0 0


Code

#include
#include
#include
#define ll long long
using namespace std;
int a[20];
ll f[20][20][20][2][2][2][2];
ll dp(int p,int last,int Last,bool flag,bool four,bool eight,bool limit)
{
	if(!p)  return flag&&(!four||!eight);
	if(~f[p][last][Last][flag][four][eight][limit])
		return f[p][last][Last][flag][four][eight][limit];
	int i,up=limit?a[p]:9;ll ans=0;
	for(i=0;i<=up;++i)
		ans+=dp(p-1,i,last,flag||(i==last&&i==Last),four||i==4,eight||i==8,limit&&a[p]==i);
	return f[p][last][Last][flag][four][eight][limit]=ans;
}
ll solve(ll x)
{
	int i,p=0;ll ans=0;
	if(x<1e10)  return 0;
	while(x)  a[++p]=x%10,x/=10;
	memset(f,-1,sizeof(f));
	for(i=1;i<=a[p];++i)
		ans+=dp(p-1,i,10,0,i==4,i==8,a[p]==i);
	return ans;
}
int main()
{
	ll l,r;
	scanf("%lld%lld",&l,&r);
	printf("%lld",solve(r)-solve(l-1));
	return 0;
}

你可能感兴趣的:(#,数位DP)