码迷,mamicode.com
首页 > 移动开发 > 详细

[CQOI2016]手机号码

时间:2020-04-30 23:23:27      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:数位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;
}

[CQOI2016]手机号码

标签:数位dp   注意   make   inline   else   进制   end   直接   read   

原文地址:https://www.cnblogs.com/chinesepikaync/p/12811409.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!