清北学堂模拟赛d6t3 反击数
分析:显然是一道数位dp题,不过需要一些奇怪的姿势.常规的数位dp能统计出一个区间内满足条件的数的个数,可是我们要求第k个,怎么办呢?转化为经典的二分问题,我们二分当前数的大小,看它是第几大的,就可以了.
显然数位dp套上模板,再用上kmp的next数组就可以了,传递4个参数:还剩下多少位没有匹配,匹配了多少位,是否达到上限和是否匹配成功,到最后判断一下就可以了.
学到了一种很强的思想:如果能求出i是第几个数,要求出第k个数就可以二分i的值.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; long long L, R, K, f[20][2010][2]; int m, nextt[40], a[30]; char X[100]; void init() { nextt[0] = 0; nextt[1] = 0; for (int i = 1; i < m; i++) { int j = nextt[i]; while (j && X[i] != X[j]) j = nextt[j]; nextt[i + 1] = X[i] == X[j] ? j + 1 : 0; } } long long dfs(int len, int w, bool limit, bool flag) { if (len == 0) return flag; if (!limit && f[len][w][flag] != -1) return f[len][w][flag]; int maxn = limit ? a[len] : 9; long long cnt = 0; for (int i = 0; i <= maxn; i++) { int t = w; while (t && X[t] - '0' != i) t = nextt[t]; if (X[t] - '0' == i) t++; cnt += dfs(len - 1, t, limit && (i == a[len]), flag || (t == m)); } return limit ? cnt : f[len][w][flag] = cnt; } long long query(long long u) { int cnt = 0; while (u) { a[++cnt] = u % 10; u /= 10; } memset(f, -1, sizeof(f)); return dfs(cnt, 0, 1, 0); } int main() { scanf("%lld %lld %s %lld", &L, &R, X, &K); m = strlen(X); init(); if (query(R) < K + query(L - 1)) { printf("Hey,wake up!\n"); return 0; } long long t = query(L - 1),ans = L; long long l = L, r = R; while (l < r) { long long mid = (l + r) >> 1; if (query(mid) - t >= K) { r = mid; ans = mid; } else l = mid + 1; } printf("%lld\n", r); return 0; }