P2022 有趣的数(二分&数位dp)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2022 有趣的数(二分&数位dp)相关的知识,希望对你有一定的参考价值。
P2022 有趣的数(二分&数位dp)
麻了。
二分答案,然后用数位dp计算 [ 1 , x ] [1,x] [1,x]中有多少个数字字典序 < < < k k k。
数位dp的时候开两个数组 a , b a,b a,b存 x , k x,k x,k。
然后就是常规的数位dp操作。
注意如果 d p dp dp数组开到 l i m , l e a d lim,lead lim,lead 则每次 c h e c k check check 都需要初始化 d p dp dp为 − 1 -1 −1。
因为这两个变量与 x x x有关。
否则可以只初始化一次。
另外 w , w 1 w,w1 w,w1长度不一定相同,所以 p o s 2 pos2 pos2可能减到 − 1 -1 −1。因此让 w 1 w1 w1基数为 20 20 20。
// Problem: P2022 有趣的数
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2022
// Memory Limit: 125 MB
// Time Limit: 400 ms
// Date: 2022-07-05 13:23:31
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll f[20][40][2][2][2],n,m;
int a[20],w;
int b[40],w1;
ll dfs(int pos, int pos2, bool cmp, bool lim, bool lead)
if (pos == 0)
return (cmp || pos2 > 20) && (!lead);
if ( f[pos][pos2][cmp][lim][lead] != -1)
return f[pos][pos2][cmp][lim][lead];
ll tmp = 0, up = lim ? a[pos] : 9;
for (int i = 0; i <= up; ++i)
bool nxcmp = !(lead && i == 0) && i < b[pos2];
nxcmp |= cmp;
if (!cmp && i > b[pos2])
continue;
tmp += dfs(pos - 1, pos2 - ((lead && i == 0) ? 0 : 1), nxcmp, lim && i == a[pos], lead && i == 0);
return f[pos][pos2][cmp][lim][lead] = tmp;
ll check(ll n)
w = 0;memset(f, -1, sizeof (f));
while (n)
a[++w] = n % 10, n /= 10;
//printf("w=%d\\n",w);
ll re = dfs(w, w1, false, true, true);
return re;
int main()
scanf("%lld%lld", &n, &m);
w1 = 20;
ll l = m > n ? m : n, r = 7e17, ans = 0;
while (n)
b[++w1] = n % 10, n /= 10;
//printf("w1=%d\\n",w1);
for (; l <= r;)
ll mid = (l + r) >> 1, tmp = check(mid);
// printf("%lld %lld\\n",mid,tmp);
if (tmp == m - 1)
ans = mid; r = mid - 1;
else if (tmp < m - 1) l = mid + 1;
else r = mid - 1;
printf("%lld\\n", ans);
以上是关于P2022 有趣的数(二分&数位dp)的主要内容,如果未能解决你的问题,请参考以下文章
CCF 201312-4有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)
poj3208 Apocalypse Someday 数位dp+二分 求第K(K <= 5*107)个有连续3个6的数。