卧薪尝胆,厚积薄发。
CQOI2016 手机号码
Date: Sat May 26 23:46:13 CST 2018 In Category: NoCategory

Description:

求 $[L,R]$ 内不含前导零,包含至少一组三个连续的数,不同时含 $4$ 和 $8$ 的数的个数。
$10^{10}\le L<R\le 10^{11}$

Solution:

数位 $DP$ , $f[i][0/1(卡边界)][j(这一位)][k(上一位)][0(没有4和8)/1(4)/2(8)/3(有4和8 不合法)][0/1(三连)]$

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
ll l,r;
#define MAXN 12
ll f[MAXN][2][11][11][4][2];//20卡边界,1:不卡边界
int a[MAXN];
int bit(int x,int y){return ((x == 8) << 1) | (x == 4) | ((y == 8) << 1) | (y == 4);}
ll calc(ll k)
{
memset(f,0,sizeof(f));
for(int i = 11;i >= 1;--i)
{
a[i] = k % 10;
k /= 10;
}
int flag = bit(a[1],a[2]),mark = 0;
//初值
//卡边界
f[2][0][a[1]][a[2]][flag][mark] = 1;
//不卡边界
for(int i = 1;i <= a[1];++i)//没有前导0且第一个数必小于等于a[1]
{
for(int j = 0;j <= 9;++j)
{
if(i == a[1] && j >= a[2])continue;//卡边界当且仅当i==a[1]&&j>=a[2]
f[2][1][i][j][bit(i,j)][0] = 1;
}
}
//转移
for(int i = 3;i <= 11;++i)
{
flag |= bit(a[i],a[i]);mark |= (a[i] == a[i - 1] && a[i - 1] == a[i - 2]);//卡边界的有没有三连
if(flag != 3)f[i][0][a[i - 1]][a[i]][flag][mark] = 1;//卡边界
//不卡边界
for(int j = 0;j <= 9;++j)//当前位
{
for(int k = 0;k <= 9;++k)//上一位
{
for(int l = 0;l <= 9;++l)//上上位
{
for(int t = 0;t <= 2;++t)//这位之前是否有48
{
int bits = t | bit(j,j),marks = (j == k && k == l);
if(bits == 3)continue;
f[i][1][k][j][bits][1] += f[i - 1][1][l][k][t][1];//之前已经有了三连 (顺推的思想)
f[i][1][k][j][bits][marks] += f[i - 1][1][l][k][t][0];//之前没有三连,这次有没有看marks
if(j < a[i])//在边界以内,则上一个可以卡边界
{
f[i][1][k][j][bits][1] += f[i - 1][0][l][k][t][1];
f[i][1][k][j][bits][marks] += f[i - 1][0][l][k][t][0];
}
}
}
}
}
}
ll ans = 0;
for(int i = 0;i <= 9;++i)
{
for(int j = 0;j <= 9;++j)
{
for(int p = 0;p <= 2;++p)
{
for(int q = 0;q <= 1;++q)
{
ans += f[11][q][i][j][p][1];
}
}
}
}
return ans;
}
int main()
{
cin >> l >> r;
if(l == 10000000000ll)cout << calc(r) << endl;
else cout << calc(r) - calc(l - 1) << endl;
return 0;
}
In tag: DP-数位DP
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡