[CQOI2016]手机号码
Posted chinesepikaync
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CQOI2016]手机号码相关的知识,希望对你有一定的参考价值。
非常模板的数位dp
状态 (f_{dep,status,fe})
我们用 (status) 来记录“要出现至少 3 个相邻的相同数字” 这个限制
若 (status=0) ,说明还没初始化
若 (status=30) ,说明已经满足了这个限制
剩下的 (status=overline{xy}) (即 (status=10 imes x+y) ) ,表示出现了连续 (x) 个 (y)
例子:(status=25) 说明出现了连续 (2) 个 (5)
然后 (fe) 来记录“号码中不能同时出现 8 和 4” 这个限制
(fe) 的二进制位从左往右第 1 位记录 4 是否出现过,第 2 位记录 8 是否出现过
例子:(status=2) (即二进制 (10) )表示没有出现过 4 但是出现过 8
关于不能有前导 0 ,在枚举数字的时候判断如果正在枚举首位那就不枚举 0
然后就是愉快的套个模板,提交,AC!
注意,如果出现 (L=10^{10}) 这种毒瘤数据要特判,直接把 (L-1) 跑数位dp就会挂
其他数位dp的题也要注意一下这种边界条件的问题
// This code wrote by chtholly_micromaker(MicroMaker)
#include <bits/stdc++.h>
#define reg register
#define int long long
using namespace std;
template <class t> inline void read(t &s)
{
s=0;
reg int f=1;
reg char c=getchar();
while(!isdigit(c))
{
if(c==‘-‘)
f=-1;
c=getchar();
}
while(isdigit(c))
s=(s<<3)+(s<<1)+(c^48),c=getchar();
s*=f;
return;
}
int f[12][31][4];
int num[100],n;
inline int dfs(int dep,int status,int fe,bool limit)
{
// fe 00: no 4 or 8 ; fe 01 : have 4 but no 8, fe 10 : have 8 but no 4; fe 11 have 4 and 8
// status : 30 is finally ; xy means the number of y is x
if(!dep)
return (status==30&&fe!=3)?1:0;
if(!limit&&~f[dep][status][fe])
return f[dep][status][fe];
// printf("%d %d %d %d
",dep,status,fe,limit);
reg int maxi=limit?num[dep]:9,res=0;
for(int i=0;i<=maxi;++i)
{
if(dep==n&&!i)
continue;
reg int nstatus=status,nfe=fe;
if(!status)
nstatus=10+i;
else if(status!=30)
{
if(status%10==i)
{
if(status/10==1)
nstatus=20+i;
else
nstatus=30;
}
else
nstatus=10+i;
}
if(i==4)
nfe|=1;
else if(i==8)
nfe|=2;
res+=dfs(dep-1,nstatus,nfe,limit&&i==maxi);
}
if(!limit)
f[dep][status][fe]=res;
return res;
}
inline int solve(int x)
{
memset(f,-1,sizeof f);
n=0;
while(x)
num[++n]=x%10,x/=10;
return dfs(n,0,0,true);
}
signed main(void)
{
reg int extra=0;
int L,R;cin>>L>>R;
if(L==10000000000ll)
++L,++extra;
cout<<solve(R)-solve(L-1)+extra<<endl;
return 0;
}
以上是关于[CQOI2016]手机号码的主要内容,如果未能解决你的问题,请参考以下文章