【BZOJ4521】[CQOI2016] *(数位DP)

点此看题面

大致题意: 问有多少个(Lsim R)的整数,满足至少有(3)个相邻的相同数字,且不能同时出现(4)(8)

数位(DP)

好久没写过数位(DP)了,结果不出所料写出一个无比智障的错误(枚举数码的时候居然写了一个小于(9)。。。)

感觉我的(DP)可能有些复杂,考虑设(f_{x,k,g,t})表示当前枚举到第(x)位,(k=0/1/2)分别表示没有出现(4)(8)出现了(4)出现了(8)(g)表示当前正在连续的数字,(t)表示连续的数字有几个。

特殊地,令(g=10,t=0)表示已经存在(3)个相邻的相同数字。

则枚举时只要单独处理(4)(8)的情况即可。

由于位数是相同的,存在前导(0)的情况会被相互抵消掉,因此懒了点没有判前导(0)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2000
#define LL long long
using namespace std;
int l,v[20];LL n,m,S[20][3][15][4];
I LL DP(CI x,CI k=0,RI g=-1,RI t=0,CI f=0)//数位DP,f表示是否严格小于
{
	if(t==3&&(g=10,t=0),f&&~S[x][k][g][t]) return S[x][k][g][t];if(!x) return g==10;LL res=0;//用g=10,t=0表示符合条件,边界直接返回
	k^2&&(f||v[x]>=4)&&(res+=DP(x-1,1,g^10?4:10,g^10?(g^4?1:t+1):0,f||v[x]>4));//单独处理4
	k^1&&(f||v[x]>=8)&&(res+=DP(x-1,2,g^10?8:10,g^10?(g^8?1:t+1):0,f||v[x]>8));//单独处理8
	for(RI i=0;i<=9;++i) i^4&&i^8&&(f||v[x]>=i)&&(res+=DP(x-1,k,g^10?i:10,g^10?(g^i?1:t+1):0,f||v[x]>i));//枚举其余数码
	return f&&(S[x][k][g][t]=res),res;//记忆化
}
I LL Get(LL x) {l=0;W(x) v[++l]=x%10,x/=10;v[l+1]=0;return memset(S,-1,sizeof(S)),DP(11);}//分到数组中然后DP
int main() {return scanf("%lld%lld",&n,&m),printf("%lld
",Get(m)-Get(n-1)),0;}//差分
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4521.html