清北学堂模拟赛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;
}

 

posted @ 2017-10-07 00:03  zbtrs  阅读(555)  评论(0编辑  收藏  举报