标签:数位dp 注意 make inline else 进制 end 直接 read
非常模板的数位dp
状态 \(f_{dep,status,fe}\)
我们用 \(status\) 来记录“要出现至少 3 个相邻的相同数字” 这个限制
若 \(status=0\) ,说明还没初始化
若 \(status=30\) ,说明已经满足了这个限制
剩下的 \(status=\overline{xy}\) (即 \(status=10\times 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\n",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;
}
标签:数位dp 注意 make inline else 进制 end 直接 read
原文地址:https://www.cnblogs.com/chinesepikaync/p/12811409.html